代码审计[一] [0CTF 2016]piapiapia

news/发布时间2024/5/19 21:01:44

代码审计[一]

[0CTF 2016]piapiapia

image-20240330222529862

对着登录框一顿乱注,发现都没什么效果,于是转向目录爆破。

gobuster不知道为什么爆不了,只能用dirsearch来了

dirsearch -u [url]  -s 1 -t 10
image-20240330223719374

爆到了一整个源码备份压缩包,下载后进行分析

image-20240330223833938

源码分析

index.php

对于html部分,可以见到是登录界面,就不贴出来了。而php代码部分:

<?phprequire_once('class.php');//引入了class.php文件
//判断用户是否已经登录,如果已登录就重定向到profile.phpif($_SESSION['username']) {header('Location: profile.php');exit;}if($_POST['username'] && $_POST['password']) {$username = $_POST['username'];$password = $_POST['password'];
//输入限制if(strlen($username) < 3 or strlen($username) > 16) die('Invalid user name');if(strlen($password) < 3 or strlen($password) > 16) die('Invalid password');
//链接到class.php的函数中,作用是和数据库中的账密匹配if($user->login($username, $password)) {$_SESSION['username'] = $username;header('Location: profile.php');exit;	}else {die('Invalid user name or password');}}else {
?>

那就往class.php方向走

class.php

这个文件的篇幅很大,就拆有用的部分出来分析。

	//注册页面验证public function register($username, $password) {//调用 父类中的filter处理值$username = parent::filter($username);$password = parent::filter($password);$key_list = Array('username', 'password');$value_list = Array($username, md5($password));//插入数据库中return parent::insert($this->table, $key_list, $value_list);}//登录页面查询public function login($username, $password) {$username = parent::filter($username);$password = parent::filter($password);$where = "username = '$username'";//在数据库中查询是否存在此信息$object = parent::select($this->table, $where);if ($object && $object->password === md5($password)) {return true;} else {return false;}}
public function filter($string) {//过滤符号 '和 \\$escape = array('\'', '\\\\');//用implode讲元素用|连起来,做成一个正则表达式模式,下面同理$escape = '/' . implode('|', $escape) . '/';$string = preg_replace($escape, '_', $string);$safe = array('select', 'insert', 'update', 'delete', 'where');$safe = '/' . implode('|', $safe) . '/i';return preg_replace($safe, 'hacker', $string);}

register.php

从class.php中,我们了解到那是一个处理各种情况下的数据库操作代码。我们并没有一个登录的环境session,目录爆出来有register页面,那就直接去注册一个。html部分是表单,就省略了,以下是php代码

<?phprequire_once('class.php');if($_POST['username'] && $_POST['password']) {$username = $_POST['username'];$password = $_POST['password'];//对username和password做了输入限制if(strlen($username) < 3 or strlen($username) > 16) die('Invalid user name');if(strlen($password) < 3 or strlen($password) > 16) die('Invalid password');//限制都通过,且数据库没有相同用户下,写入注册数据到数据库中,重定向到index.phpif(!$user->is_exists($username)) {$user->register($username, $password);echo 'Register OK!<a href="index.php">Please Login</a>';		}else {die('User name Already Exists');}}else {
?>

重定向到index.php,成功登录验证后会跳转到profile.php

profile.php

打开一看是个详细信息补全页面,html是展出信息,所以依然省略。以下是php代码

<?phprequire_once('class.php');if($_SESSION['username'] == null) {die('Login First');	}$username = $_SESSION['username'];//对session中的用户到数据库中查询对应信息$profile=$user->show_profile($username);if($profile  == null) {//若没有任何信息,重定向到update.phpheader('Location: update.php');}else {//若有信息,则反序列化$profile$profile = unserialize($profile);$phone = $profile['phone'];$email = $profile['email'];$nickname = $profile['nickname'];//注意到有一个file_get_contents函数$photo = base64_encode(file_get_contents($profile['photo']));
?>

现在找到了一个能文件包含的函数,结合起序列化,我们可以做一个字符逃逸的反序列化payload,但是现在不知道包含些什么文件,继续往下看。

update.php

同样,html是表单部分,不看。以下是php代码

<?phprequire_once('class.php');if($_SESSION['username'] == null) {die('Login First');    }if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {$username = $_SESSION['username'];//各种输入过滤验证if(!preg_match('/^\d{11}$/', $_POST['phone']))die('Invalid phone');if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))die('Invalid email');if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)die('Invalid nickname');$file = $_FILES['photo'];if($file['size'] < 5 or $file['size'] > 1000000)die('Photo size error');//查了一下这个函数,是讲上传文件移动到指定位置的//$file['tmp_name']    --原路径//'upload/' . md5($file['name']) --upload文件下用md5命名每个文件move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));$profile['phone'] = $_POST['phone'];$profile['email'] = $_POST['email'];$profile['nickname'] = $_POST['nickname'];$profile['photo'] = 'upload/' . md5($file['name']);//看到这里md5,本来想试试ffifdyop的,但是跟着update_profile跳转后,发现符号'是被过滤的,动不了手脚//那方向明确了,就是反序列化字符逃逸,但是该包含什么文件?$user->update_profile($username, serialize($profile));echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';}?>

config.php

最后一个文件,一看源码,豁然开朗了。那就文件包含config.php

<?php$config['hostname'] = '127.0.0.1';$config['username'] = 'root';$config['password'] = '';$config['database'] = '';$flag = '';
?>

反序列化字符逃逸

分析filter函数,可以看到特定字符会被过滤为hacker,而hacker有6个字符,特定字符数据中,有5/6位的字符,那么可以构成一个字符增加利用

public function filter($string) {//过滤符号 '和 \\$escape = array('\'', '\\\\');//用implode讲元素用|连起来,做成一个正则表达式模式,下面同理$escape = '/' . implode('|', $escape) . '/';$string = preg_replace($escape, '_', $string);$safe = array('select', 'insert', 'update', 'delete', 'where');$safe = '/' . implode('|', $safe) . '/i';return preg_replace($safe, 'hacker', $string);}

这里贴上我之前的些笔记

字符增加的利用

$data= 'O:4:"test":2:{s:2:"v1";s:48:"lslslslslsls";s:2:"v2";s:16:"system("whoami")";}";s:2:"v3";s:3:"123";}';
//我们可以得知字符替换后长度会增加6,那么我们可以做一些敏感命令藏在语句当中。
//比如";s:2:"v2";s:16:"system("whoami")";}这一段
$data=str_replace("ls","nohacker",$data);//2->8 eat 6
var_dump(unserialize($data));

输出结果:

object(test)#1 (2) {["v1"]=>string(48) "nohackernohackernohackernohackernohackernohacker"["v2"]=>string(16) "system("whoami")"
}

做题

重新来看看这两个

$profile['phone'] = $_POST['phone'];$profile['email'] = $_POST['email'];$profile['nickname'] = $_POST['nickname'];$profile['photo'] = 'upload/' . md5($file['name']);$user->update_profile($username, serialize($profile));
	$profile = unserialize($profile);$phone = $profile['phone'];$email = $profile['email'];$nickname = $profile['nickname'];$photo = base64_encode(file_get_contents($profile['photo']));

并起来一起看就有头绪了,可以在nickname的输入中用字符逃逸的方法挤一个config.php给photo,然后从profile.php界面图片的base64解码内容就是config.php的所有内容

构造payload

因为一开始忘记nickname有输入限制,导致我构造出来的一直错误。所以看了网上一圈wp,发现还能用数组方式来绕过,学到了。

nickname[]=where*34";}s:5:"photo";s:10:"config.php";}//只能是where,其他都是6位字符

经过filter函数后变成

nickname[]=hacker*34";}s:5:"photo";s:10:"config.php";}

增加的34个字符,恰好把";}s:5:"photo";s:10:"config.php";}吐出来。而对于为什么前面会有{看到了一篇wp做了解析才明白,数组在序列化中样式会有所不同

[0CTF 2016]piapiapia WP(详细)_[0ctf 2016]piapiapia wp-CSDN博客

实操

注册-->登录-->profile开始抓包

db5d600b6c4085d3cffabd86c6889e9

放包后访问profile.php,查看图片源码,base64解码

7a0655bf0a8fe49a89150f08645f50e

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

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

Yolov 体验

参考Yolov 中文文档 yolov8_wpf_example(简单搜索到的一个示例程序) 本文源码【下载】环境软件/系统 版本 说明Windows Windows 10 专业版 22H2 19045.4170Microsoft Visual Studio Microsoft Visual Studio Community 2022 (64 位) - 17.6.5Microsoft .Net SDK 8.0.101 手动…

第35章: 中间件塔尖实战—穿透Netty对象池、内存池,内存泄露排查

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,…

Spring中如何解决循环依赖

八字真言:“三级缓存,提前暴露” 此文只是介绍简单的情况便于理解,实际上场景会更复杂、情况会更多,但是原理相通。 一、什么是循环依赖? 从字面上来理解就是A依赖B的同时B也依赖了A,就像下面这样上图是简单的循环依赖,也会存在A依赖B,B依赖C,C依赖A这种循环,或者更复…

Postgresql同步数据到Elasticsearch

Postgresql同步数据到es需要借助中间工具连接器,连接器部署主要有两种方式,一种是基于Elastic云托管的连接器(Native connectors), 另外一种自己安装管理的连接器(self-managed connector). 托管方式连接器的使用方法文档:https://www.elastic.co/guide/en/enterprise-sear…

2024年3月30号java学习

集合集合中的长度不固定,集合中只能存储引用数据类型,不能够存放基本数据类型,需要变成基本数据类型对应的包装类。 基本数据类型的包装类ArrayList ArrayList<类型> 变量名 = new ArrayList<>();示例代码 package arrayList;import java.util.ArrayList;public …

ESP8266使用笔记

ESP8266介绍上图所示的是ESP-01s,我目前使用的是由安信可科技公司开发的模块,搭有板载天线。可以由AP/STA/AP+STA三种工作模式。其中AP工作模式是作为WIFI的客户端存在,其他设备主动链接在AP设备上,可以接收AP设备发送的消息,也可以反过来给AP设备发送消息。 STA工作模式则…