本人参与极客大挑战2023的出题工作,主要负责密码学板块。
对于GoGoCrypto这道反响比较高的题目,这里附上题解。
题目源码
这题用Golang写了一个Flag Folder的交互,流程大概长这样:
image-20231227004357980
加解密采用的算法是AES的CBC mod。
image-20231227004502286
这种加密算法的主要攻击手段是字节翻转(bit
flipping) ,这种攻击的前提是一个明文分组是已知的。
sid是16字节,根据填充算法:
1 2 3 4 5 func Pad (pt []byte ) []byte { padlen := aes.BlockSize - (len (pt) % aes.BlockSize) padding := bytes.Repeat([]byte {byte (padlen)}, padlen) return append (pt, padding...) }
可以知道填充后有两块:
1 2 3 4 block#1 block#2 +-----------------+-----------------+ | sid | Padding ("\x10"*16) | +-----------------+-----------------+
那么我们就能修改第一个密文分组,来控制解密后的第
二个明文分组。具体的原理是:
image-20231227004700688
这一步显然会破坏第一块的明文,但并不影响我们的利用。
1 2 3 4 func Unpad (pt []byte ) []byte { padlen := int (pt[len (pt)-1 ]) return pt[:len (pt)-padlen] }
unpad函数只是将padding的最后一个字节作为padding长度,根本不检查,所以我们可以很容易的让padding长度为len(plaintext)
- 1,那么我们就可以得到一个只有一个字节的明文了!
因此攻击流程是:
修改密文并上传
服务端解密,得到只有一个字节的明文
遍历所有明文(254),必然得到flag。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import os from urllib.parse import unquote from base64 import b64decode as dec, b64encode as enc from pwn import xor import requestsfrom hashlib import sha512 def curl_request (url, method='GET' , headers=None , data=None ): try : if method.upper() == 'GET' : response = requests.get(url, headers=headers) elif method.upper() == 'POST' : response = requests.post(url, headers=headers, data=data) elif method.upper() == 'PUT' : response = requests.put(url, headers=headers, data=data) elif method.upper() == 'DELETE' : response = requests.delete(url, headers=headers, data=data) else : print ("Unsupported HTTP method" ) return None response.raise_for_status() return response.text except requests.exceptions.RequestException as e: print (f"Error: {e} " ) return None url = 'http://47.109.106.62:7842/' response = requests.get(url) x = str (response.headers) token = unquote(x.split("token" )[1 ].split(";" )[0 ][1 :]) token = dec(token) nonce = unquote(x.split("nonce" )[1 ].split(";" )[0 ][1 :]) c1, c2 = token[:len (token)//2 ], token[len (token)//2 :] c1 = xor(xor(c1,b'\x10' *16 ),b'\x1f' *16 ) form_data = { "Rec" : enc(c1+c2).decode() } res = curl_request('http://47.109.106.62:7842/api/dec' , method='POST' , headers=None , data=form_data) print (res)for i in range (256 ): form_data = { "Password" : enc(chr (i).encode()).decode()+enc(sha512(dec(nonce)).digest()).decode() } res = curl_request('http://47.109.106.62:7842/api/check' , method='POST' , headers=None , data=form_data) if "SYC" in str (res): print (res) break