ctfshow-Web应用安全与防护challenge做题笔记 长期更新

Base64多层嵌套解码

1) 题目信息收集

目标站点打开后是一个登录页,username 固定为 admin,前端提交到 check.php

页面里有一段关键 JS:

  • 定义了 correctPassword = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU="
  • 对输入密码做多次 btoa 变换后比较是否等于上面常量

说明考点是前端混淆逆向 + 请求构造,不是传统爆破。


2) 分析前端加密逻辑

JS 逻辑(简化):

  1. encoded = btoa(input)
  2. encoded = btoa(encoded + 'xH7jK').slice(3)
  3. encoded = btoa(reverse(encoded))
  4. encoded = btoa('aB3' + encoded + 'qW9').substr(2)
  5. 判断 btoa(encoded) === correctPassword

要拿到明文密码,就反着解这条链(逆向 base64 + 去前后缀 + reverse)。

反解思路(逆序)

设最后比较前的变量叫 encoded4,则:

  1. btoa(encoded4) = correctPassword

    => encoded4 = atob(correctPassword)

  2. 正向第4步是:encoded4 = btoa('aB3' + encoded3 + 'qW9').substr(2)

    逆向时把被截掉的前2位补回去再 atob

    • tmp = 'YU' + encoded4(这题补出来恰好是 YU
    • raw = atob(tmp)
    • 去前后缀:encoded3 = raw.slice(3, -3)(去掉 aB3qW9
  3. 正向第3步:encoded3 = btoa(reverse(encoded2))

    逆向:

    • rev = atob(encoded3)
    • encoded2 = reverse(rev)
  4. 正向第2步:encoded2 = btoa(encoded1 + 'xH7jK').slice(3)

    逆向时要补回丢失的前3个 base64 字符(可爆破 64^3):

    • 枚举 p in [A-Za-z0-9+/]{3}
    • raw2 = atob(p + encoded2)
    • 保留满足 raw2xH7jK 结尾的候选
    • encoded1 = raw2.slice(0, -5)
  5. 正向第1步:encoded1 = btoa(input)

    逆向:input = atob(encoded1)

最后得到输入密码字节:fb e1 37 33 31 36(显示上近似 ûá7316)。

最终可以还原出密码对应字节为:

  • fb e1 37 33 31 36

可视作 ûá7316,但这里有编码坑(见下)。


3) 后端额外限制点

直接 POST 会提示:

  • Invalid User-Agent
  • 提示必须使用:ctf-show-brower(原题拼写)

所以请求头必须带:

  • User-Agent: ctf-show-brower

4) 编码坑(关键)

即使你填 ûá7316,很多情况下仍会 Invalid password,原因是:

  • 浏览器/常规表单多按 UTF-8 提交:ûá -> %C3%BB%C3%A1
  • 题目实际按单字节(latin1)语义比对,需要 %FB%E1

因此要手动发原始字节编码的表单值。


5) 最终利用请求

curl -A "ctf-show-brower" -X POST \

"https://7f91e136-dc72-4b92-8a46-91c77eddc4e0.challenge.ctf.show/check.php" \

-H "Content-Type: application/x-www-form-urlencoded" \

--data "username=admin&password=%FB%E17316"


6) 回显与 Flag

返回成功信息:

  • Login successful! Flag: CTF{base64_brute_force_success}

最终 Flag:

CTF{base64_brute_force_success}


7) 题目小结

这题本质是三步:

  • 前端混淆逆向出密码字节
  • 满足后端 User-Agent 校验
  • 处理字符编码差异(UTF-8 vs latin1)

cookie伪造

一句话木马变形

题目现象与第一判断

打开题目网址后,是一个"PHP Code Executor",提交 code 后服务端会执行并回显结果。直觉上这是 RCE/代码执行,目标就是读到 flag。

第一步:摸清限制(黑盒测试)

先随便提交一些常见 payload:

  • echo 'OK';(含引号)
  • var_dump(1,2);(含逗号)
  • readfile('flag.php');(含点号、引号)

结果会提示类似:

  • Invalid characters detected! Only letters, numbers, underscores ,parentheses and semicolons are allowed.

结论:后端做了 白名单过滤,大概率只允许字符集:

  • 字母数字:A-Za-z0-9
  • 下划线:_
  • 括号:()
  • 分号:;

这意味着我们不能直接使用:

  • 字符串(没引号)
  • 变量(没 $
  • 拼接/路径(没 ./、空格)
  • 多参数函数(没 ,

所以"直接读 /flag、直接 cat、直接 $flag"这类常规思路会卡死。

第二步:用"可用字符"做信息收集

既然能执行 PHP,那就用不需要引号/逗号/点号的函数先探环境:

  • phpinfo();
    目的:确认 PHP 版本、DOCUMENT_ROOTdisable_functions、是否 open_basedir 限制等。
  • print_r(scandir(getcwd()));
    目的:列出当前目录文件名(这是后续绕过的关键)。

getcwd()

获取当前工作目录(Get Current Working Directory)

返回 PHP 脚本当前所在的绝对路径,例如:

复制代码
/var/www/html

scandir()

扫描目录 ,列出指定目录下的所有文件和子目录(包括 ...

参数:目录路径 返回值:数组,包含文件名

例如 scandir('/var/www/html') 返回:

复制代码
Array
(
    [0] => .
    [1] => ..
    [2] => index.php
    [3] => config.php
    [4] => uploads
)

打印数组/对象的可读格式(Print Readable)

把数组内容以人类可读的方式输出,常用于调试。


组合起来的作用
复制代码
print_r(scandir(getcwd()));

列出当前 PHP 脚本所在目录下的所有文件和文件夹。

执行后能看到当前目录类似:

  • flag.php
  • index.php

到这里就知道:flag 很可能就在 flag.php 里。

第三步:绕过核心------"不能手写文件名,就让 PHP 给你生成"

限制里最烦的是 不能输入 flag.php(因为有 .),也不能写字符串传参。

但我们已经能 scandir(getcwd()) 拿到一个数组,数组里天然包含字符串 flag.php

思路变成:

不自己写 flag.php,而是从 scandir() 的结果里把它"取出来"。

目录数组常见是:

  • ['.','..','flag.php','index.php']

然后用只需一个参数的函数做变换:

  • array_reverse(scandir(getcwd())) 得到:['index.php','flag.php','..','.']
  • next(...) 取第二个元素,刚好是 flag.php

验证文件名是否取对:

var_dump(next(array_reverse(scandir(getcwd()))));

输出 string(8) "flag.php",说明成功"无点号构造文件名"。

第四步:读 flag(不需要 $flag、不需要引号)

现在我们已经拿到了文件名字符串(由 PHP 产生),接下来只要把它喂给一个"读文件/显示源码"的函数即可。

这里用 show_source() / highlight_file() 都行(单参数,且会输出内容):

show_source()

复制代码
show_source('index.php');
  • 读取指定文件内容
  • 用语法高亮(彩色)显示 PHP 代码
  • 等同于 highlight_file()

highlight_file()

复制代码
highlight_file('/var/www/html/config.php');
  • 功能与 show_source() 完全相同
  • 是官方推荐使用的别名

show_source(next(array_reverse(scandir(getcwd()))));

它会把 flag.php 源码直接回显,里面就能看到类似:

  • $flag = "CTF{...}"

最终拿到 flag:

CTF{shell_code_base64_bypass}


总结(可迁移套路)

这题的关键点是:受限字符集的代码执行时,不要执着于"我手写 payload",而要想:

  • 利用函数返回值生成字符串(比如 scandir()getcwd()phpinfo() 输出内容)
  • 通过数组/迭代器操作取出目标字符串(array_reverse()next()current() 等)
  • 再把"生成的字符串"传给读文件函数(show_source()/readfile() 等)

一句话:输入受限 → 让程序替你"造出你需要的字符串"。

相关推荐
想成为优秀工程师的爸爸10 小时前
第十九篇技术笔记:UDP——相思传得快,飞鸽传书在
笔记·网络协议·tcp/ip·udp·信息与通信
Yeh20205814 小时前
cookie与Session笔记
笔记
d111111111d14 小时前
STM32-UART封装问题解析
笔记·stm32·单片机·嵌入式硬件·学习·算法
寒秋花开曾相惜15 小时前
(学习笔记)4.2 逻辑设计和硬件控制语言HCL(4.2.1 逻辑门&4.2.2 组合电路和HCL布尔表达式)
linux·网络·数据结构·笔记·学习·fpga开发
菩提小狗15 小时前
每日安全情报报告 · 2026-04-24
网络安全·漏洞·cve·安全情报·每日安全
C2H5OH15 小时前
PortSwigger SQL注入LAB4
网络安全
Yeh20205815 小时前
request与response笔记
java·前端·笔记
Fuyo_111916 小时前
C++ 内存管理
c++·笔记
柳鲲鹏16 小时前
李善兰和牛顿,谁剽窃谁的运动三定律
笔记