CTFshow __Web应用安全与防护 第一章
一、Base64编码隐藏
在做题之前我们先来了解一下Base64编码的实现过程。
Base64顾名思义就是基于64个字符来表示二进制数据的一种编码方式,而这64个字符是:
A-Z (26个) 大写字母
a-z (26个) 小写字母
0-9 (10个) 数字
/ 和 + (2个) 特殊符号
= (填充字符) 不是64个字符之一,用于填充
编码过程
1.字符串转换为ASCII码
2.ASCII码转换为二进制(每字符8位)
3.将8位二进制重新分组为6位一组
4.每组6位二进制转换为十进制
5.通过查base64编码表匹配对应字符,生成编码结果
字符串 "cai" 的 Base64 编码过程
|-----------------------------------------------|--------------------------------------------------|
| **阶段** | **具体内容** |
| 原始字符串 | cai |
| 字符 -> ASCII码 | c -> 99、a -> 97、i -> 105 |
| ASCII -> 8位二进制 | 99 -> 01100011、97 -> 01100001、105 -> 01101001 |
| 24位二进制拼接结果 | 011000110110000101101001 |
| 6位分组(4组) | 第1组:011000、第2组:110110、第3组:000101、第4组:101001 |
| 分组 -> 十进制索引 | 24、54、5、41 |
| 索引 -> Base64字符(A-Z:0-25,a-z:26-51,0-9:52-61) | 24 -> Y、54 -> 2、5 -> F、41 -> p |
| 最终Base64编码 | Y2Fp |
登录页面;

F12看原代码;

阅读代码,correctPassword 定义了密码 "Q1RGe2Vhc3lfYmFzZTY0fQ==",继续看下面的代码,Flag应该就是密码;
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const correctPassword = "Q1RGe2Vhc3lfYmFzZTY0fQ==";
const enteredPassword = document.getElementById('password').value;
const messageElement = document.getElementById('message');
if (btoa(enteredPassword) === correctPassword) {
messageElement.textContent = "Login successful! Flag: "+enteredPassword;
messageElement.className = "message success";
} else {
messageElement.textContent = "Login failed! Incorrect password.";
messageElement.className = "message error";
}
});
初步判断是Base64编码,使用Burp进行解码,结果是 "CTF{easy_base64}",这是密码;

使用解码后的密码登录

Flag = "CTF{easy_base64}"
二、HTTP头注入
前几步与 "Base64编码隐藏" 相同,但使用密码登录后提示登录失败

"Invalid User-Agent":"无效的用户代理"
You must use "ctf-show-brower" browser to access this page:"您必须使用 "ctf-show-brower" 浏览器才能访问此页面"
那就很清楚了,需要让服务器认为我们使用的是 "ctf-show-brower" 浏览器,使用Burp修改HTTP头

Flag = "CTF{user_agent_inject_success}"
三、Base64多层嵌套解码
登录页面;

F12查看原代码;

阅读代码,correctPassword 定义了密码 "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU=",继续看下面的代码,函数 "validatePassword" 对输入的密码进行了4次加密;
document.getElementById('loginForm').addEventListener('submit', function(e) {
const correctPassword = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU=";
function validatePassword(input) {
let encoded = btoa(input);
encoded = btoa(encoded + 'xH7jK').slice(3);
encoded = btoa(encoded.split('').reverse().join(''));
encoded = btoa('aB3' + encoded + 'qW9').substr(2);
return btoa(encoded) === correctPassword;
}
const enteredPassword = document.getElementById('password').value;
const messageElement = document.getElementById('message');
if (!validatePassword(enteredPassword)) {
e.preventDefault();
messageElement.textContent = "Login failed! Incorrect password.";
messageElement.className = "message error";
}
});
加密步骤表格
|------------|-------------------|------------------|----------------------------------------------|-----------------------------------------------------------------------------------|
| **步骤** | **操作描述** | **函数/方法** | **参数/操作详情** | **输入/输出示例** |
| 1 | 初始Base64编码 | btoa() | 对原始密码字符串直接编码 | "test" -> "dGVzdA==" |
| 2 | 添加后缀+二次Base64+截取 | btoa().slice(3) | 1. 拼接固定后缀 "xH7jK" 2. 对结果进行Base64编码 3. 移除前3字符 | "dGVzdA==" -> "dGVzdA==xH7jK" -> "ZEdWemRBPT14SDdqSw==" -> "WemRBPT14SDdqSw==" |
| 3 | 反转字符串+三次Base64 | btoa(反转) | 1. 字符串转为字符数组 2. 反转数组顺序 3. 合并回字符串 4. Base64编码 | "WemRBPT14SDdqSw==" -> 反转 -> "==WSqdjHD41TPBmeW" -> "PT1XU3FkakhENDFUUEJtZVc=" |
| 4 | 添加前后缀+四次Base64+截取 | btoa().substr(2) | 1. 前加 "aB3",后加 "qW9" 2. Base64编码 3. 移除前2字符 | "aB3" + encoded + "qW9" -> Base64 -> 截取前2字符 |
| 5 | 最终Base64编码 | btoa() | 对第4步结果进行Base64编码 | 生成最终密文 |
| 6 | 比对验证 | === | 与硬编码密文比较 | btoa(encoded) === "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1 UwNXFSWGRVVlZrOWNWYzU=" |
使用AI反推出脚本,得出密码为 "017316"
python
import base64
import itertools
def brute_force_numeric_password():
"""暴力破解 4-8 位数字密码"""
target = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU="
#尝试 4-8 位数字
for length in range(4, 9):
print(f"尝试 {length} 位数字密码...")
#生成所有可能的数字组合
for digits in itertools.product("0123456789", repeat=length):
password = "".join(digits)
# 模拟原验证逻辑
encoded = base64.b64encode(password.encode()).decode()
encoded = base64.b64encode((encoded + "xH7jK").encode()).decode()[3:]
encoded = base64.b64encode(encoded[::-1].encode()).decode()
encoded = base64.b64encode(("aB3" + encoded + "qW9").encode()).decode()[2:]
final = base64.b64encode(encoded.encode()).decode()
if final == target:
return password
return None
#运行暴力破解
password = brute_force_numeric_password()
print(f"找到的密码: {password}")
使用密码 "017316" 登录,提示错误,如 "HTTP头注入",使用Burp修改HTTP头

Flag = "CTF{base64_brute_force_success}"
四、HTTPS中间人攻击
这是是一道静态分析题,打开页面下载web4.zip到本地,里面有target.pcap和sslkey.log两个文件
target.pcap是Wireshark的流量包文件,而sslkey.log则是密钥日志文件,使用Wireshark加载流量包
可以看到TLS三次握手后,但在应用层的数据传输都被加密,这也是HTTPS与HTTP不同的地方,在TLS握手阶段,Client和Server发送和回复hello时已经确定好了加密套件,双方基于非对称加密交换预主密钥,并形成主密钥。而在数据传输阶段,客户端和服务端会使用协商好的对称加密算法和会话密钥对后续应用层传输的数据进行加密,解密TLS流量的本质就是拿到主密钥或者会话密钥,该题给出的sslkey.log属于密钥日志,是浏览器/客户端把会话密钥写入日志中得到的文件,可以用来进行解密。

打开Wireshark,依次点击:编辑 -> 首选项 -> Protocols,找到TLS选项,在 "(Pre)-Master-Secret log filename" 中导入获得的密钥

POST数据包中即可拿到Flag

Flag = "CTF{https_secret_data}"
五、Cookie伪造
打开靶场,猜测弱密码guest/guest登陆进去,提示信息 "Login successful! welcome guest user",但没有Flag,猜测需要使用Admin登录才可以拿到Flag

使用Burp抓包,发现Cookie中有 "role" 字段,字段值为 "guest",修改为 "admin"

登录进去拿到Flag

Flag = "CTF{cookie_injection_is_fun}"