2026.3.22 PolarCTF Misc
2026-08-22 20:25:00

time

1
2
3
key: 长度为 5\4
线索: 开机时间戳 1630416000
ptdh{dqpfsajpsvjgSVgbVQIFLWXZ}

看到 ptdh{...} 联想到flag 格式flag{...}
说明 ptdh 对应 flag,这是一种字母替换加密。

又因为提示说”key 长度为 5\4”,说明这不是简单的凯撒密码(因为凯撒只有一个固定偏移量),
而是维吉尼亚密码——用一个多字符的密钥循环加密

关于维吉尼亚密码

字母编号 a=0 到 z=25,公式就一行:

1
密文 = (明文 + 密钥) mod 26       明文 = (密文 - 密钥 + 26) mod 26

推导 key:密文前4个 p t d h 对应明文(flag 格式)f l a g,反推:

1
密钥[0] = (密文 - 明文 + 26) mod 26

密钥 = kidb(长度4,循环使用)

{} 是特殊符号,不参与加密,直接保留原样

解密

大小写规则:密文是大写字母 → 明文也是大写;密文是小写字母 → 明文也是小写

timeisgoingfINdaLIFEBOUY

flag{timeisgoingfINdaLIFEBOUY}

时间正在流逝,寻找救生圈

p.s.随波逐流7.2对于维吉尼亚密码是大小写不分的 可以使用在线网址

http://moersima.00cha.net/wjly.asp

看了一下讲解视频 实际上是有推导链的

第一步:时间戳转日期

16304160002021-08-31 21:12:00(北京时间)

第二步:提示 key

长度为 5\4 的意思是:从日期中挑出5个数字,拼成4个数

从 2021-08-31 中取:

1
2
3
4
5
6
年份 2021 → 取最后一个 "1"
月份 08 → 取 "0" 和 "8"
日期 31 → 取 "3" 和 "1"
5个数字:1, 0, 8, 3, 1
↓ 拼成4个数(10用了两位,其余各用一位)
10 8 3 1

第三步:数字转字母(a=0 起)

1
2
3
4
5
10 → k
8 → i
3 → d
1 → b
密钥 = kidb

这个大概真想不到

麦填

jpg文件 010打开

jpg文件 文件头是FF D8 FF文件尾是FF D9CTRL+F找到FF D9后面有一段base64 解码后是英文789

保存后面一大段png图片 扫描二维码 获得flag{win

flag{win789}

隐藏的二维码

使用Python遍历图片像素并提取LSB通道

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from PIL import Image
import argparse
import os

def extract_lsb(input_path, output_path):
"""
遍历图片像素,提取LSB通道并生成新图片。
新图片中,如果原像素某通道的LSB为1,则设为255(白);如果为0,则设为0(黑)。
这样可以清晰地看到隐藏的数据模式。
"""

if not os.path.exists(input_path):
print(f"错误: 找不到文件 {input_path}")
return

try:
img = Image.open(input_path)
except Exception as e:
print(f"错误: 无法打开图片 {e}")
return

# 确保图片是 RGB 模式,如果不是则转换(处理 RGBA 或灰度图)
if img.mode != 'RGB':
img = img.convert('RGB')

width, height = img.size
print(f"正在处理图片: {input_path} ({width}x{height})...")

# 创建一个新的空白图片用于存放结果
lsb_img = Image.new('RGB', (width, height))

# 获取像素访问对象
pixels = img.load()
lsb_pixels = lsb_img.load()

# 遍历每一个像素
for y in range(height):
for x in range(width):
r, g, b = pixels[x, y]

# 提取每个通道的最低有效位 (LSB)
# & 1 运算可以获取二进制最后一位 (0 或 1)
lsb_r = r & 1
lsb_g = g & 1
lsb_b = b & 1

# 将提取出的位放大到 0 或 255 以便肉眼观察
# 如果位是 1 -> 255 (白色), 如果位是 0 -> 0 (黑色)
new_r = 255 if lsb_r else 0
new_g = 255 if lsb_g else 0
new_b = 255 if lsb_b else 0

lsb_pixels[x, y] = (new_r, new_g, new_b)

# 保存结果
lsb_img.save(output_path)
print(f"LSB 提取完成!结果已保存至: {output_path}")

if __name__ == "__main__":
# 设置命令行参数解析
parser = argparse.ArgumentParser(description="像素级分析:提取图片的LSB通道")
parser.add_argument("input_image", help="输入图片的路径")
parser.add_argument("-o", "--output", default="lsb_output.png", help="输出图片的路径 (默认: lsb_output.png)")

args = parser.parse_args()

extract_lsb(args.input_image, args.output)

flag{qrc0de_1s_h1dden_1n_p1xels}

做日志分析类题目的几个概念:

  • nginx_access.log:记录所有 HTTP 请求,格式为:IP - - [时间] "请求方法 路径 协议" 状态码 响应大小 "来源页" "浏览器"
  • mysql_general.log:记录数据库执行的每一条 SQL 语句
  • opencart_error.log:OpenCart 网站自己写的日志,包含登录成功/失败等应用层事件
  • auth.log:Linux 系统的认证日志,记录 SSH 登录等
  • 状态码:200 = 成功,302 = 跳转,404 = 找不到,403 = 禁止访问

先看看每个文件有多少行,心里有个底:

1
Get-ChildItem *.log | ForEach-Object { "{0,-30} {1}" -f $_.Name, (Get-Content $_.FullName).Count }
1
2
3
4
5
auth.log                       371
mysql_general.log 267
nginx_access.log 1835
nginx_error.log 242
opencart_error.log 544

nginx访问日志最多,是主要分析对象。再看一下README了解背景

1
外部攻击者对 /admin 进行探测与登录尝试,随后通过后台文件管理器(filemanager)进行可疑上传……

就可以知道 攻击方向是/admin后台

attack_log

题目一:完成后台登录成功的攻击IP地址为?

后台登录成功。意味着有人通过POST请求提交账号密码。并且登录成功了

搜索被重复请求最多的路径,就是OpenCart的后台登录接口。

1
2
3
Get-Content nginx_access.log | Select-String "POST" | ForEach-Object {
($_.Line -split '"')[1]
} | Group-Object | Sort-Object Count -Descending | Select-Object -First 15 | Select-Object Count, Name | Format-Table -Wrap
1
2
3
4
5
Count Name
----- ----
27 POST /admin/index.php?route=common/login HTTP/1.1
1 POST /image/catalog/.cache_hidden_218.php HTTP/1.1
1 POST /admin/index.php?route=common/filemanager/upload&user_token=7f2d...ab91 HTTP/1.1

OpenCart后台登录接口是/admin/index.php?route=common/login

搜索route=common/login并保存到文件(此处为什么不用完整的接口 因为?在命令行里是特殊字符 会引起问题 截取一段特殊的字符效果也可以)

1
2
findstr "POST.*route=common/login" nginx_access.log > login.txt
notepad login.txt

两个IP在发送请求

  • 39.98.12.44发了大量的POST 应该是在爆破
  • 45.133.12.77只发了一次POST

这两个二选一。现在要判断哪个IP实际上登录成功了

实际上登录成功后,OpenCart会跳转到后台主页。url里会带一个user_token参数

ngnix_access.log里ctrl+F 45.133.12.77

1
2
3
4
5
45.133.12.77 - - [18/Feb/2026:01:28:52 +0800] "POST /admin/index.php?route=common/login HTTP/1.1" 200 8342 "http://47.95.21.47/admin/index.php?route=common/login" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:28:54 +0800] "GET /admin/index.php?route=common/dashboard&user_token=7f2d...ab91 HTTP/1.1" 200 20144 "http://47.95.21.47/admin/index.php?route=common/login" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:28:59 +0800] "POST /admin/index.php?route=common/filemanager/upload&user_token=7f2d...ab91 HTTP/1.1" 200 412 "http://47.95.21.47/admin/index.php?route=common/filemanager" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:49:20 +0800] "GET /image/catalog/.cache_hidden_218.php HTTP/1.1" 200 87 "-" "curl/7.68.0"
45.133.12.77 - - [18/Feb/2026:01:49:27 +0800] "POST /image/catalog/.cache_hidden_218.php HTTP/1.1" 200 39 "-" "curl/7.68.0"

可以看到

  • POST登录成功(200)
  • 立刻获得了真实的user_token``7f2d...ab91
  • 随后上传了文件到filemanager也就是文件管理助手那里
  • 最后访问了一个php文件(上传的webshell)

反观39.98.12.44。它的请求里user_token一直都是admin123,是一个固定字符串,不是真实的会话token,说明它一直没登录成功,只是在暴力猜密码

二次验证

搜索"login"忽略大小写

1
findstr /I "login" opencart_error.log
1
2
2026-02-18 00:41:12 - Warning: Login failed for username 'admin' from 103.216.40.77
2026-02-18 01:08:01 - Info: Admin login success for username 'admin' from 45.133.12.77

明确写了45.133.12.77是登录成功

flag{45.133.12.77}

题目二:完成后台登录成功的攻击IP首次访问后台登录入口的时间为?

ctrl+F 45.133.12.77

1
IP - - [时间] "请求方法 路径 协议" 状态码 响应大小 "Referer" "User-Agent"
1
2
3
4
5
45.133.12.77 - - [18/Feb/2026:01:28:52 +0800] "POST /admin/index.php?route=common/login HTTP/1.1" 200 8342 "http://47.95.21.47/admin/index.php?route=common/login" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:28:54 +0800] "GET /admin/index.php?route=common/dashboard&user_token=7f2d...ab91 HTTP/1.1" 200 20144 "http://47.95.21.47/admin/index.php?route=common/login" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:28:59 +0800] "POST /admin/index.php?route=common/filemanager/upload&user_token=7f2d...ab91 HTTP/1.1" 200 412 "http://47.95.21.47/admin/index.php?route=common/filemanager" "Mozilla/5.0"
45.133.12.77 - - [18/Feb/2026:01:49:20 +0800] "GET /image/catalog/.cache_hidden_218.php HTTP/1.1" 200 87 "-" "curl/7.68.0"
45.133.12.77 - - [18/Feb/2026:01:49:27 +0800] "POST /image/catalog/.cache_hidden_218.php HTTP/1.1" 200 39 "-" "curl/7.68.0"

Referer意思是“我是从哪个页面发出这个请求的”

正常用户登录流程:

  1. GET /admin/login → 浏览器加载登录页面
  2. 填写账号密码,点提交
  3. POST /admin/login → 浏览器发送表单,Referer 自动填为上一步的页面地址

所以Referer是登录页,说明攻击者在POST之前已经先GET访问过登录页了,只是那次 GET 请求恰好没被日志记录下来

所以nginx日志能找到的最早访问后台登录入口的记录都是这一条

flag{2026-02-18 01:28:52}

题目三:攻击者探测的敏感环境配置文件路径

攻击者在尝试登录之前,通常会先”侦察”目标服务器,看看有没有泄露的配置文件。常见的敏感文件有:

  • .env:环境配置,常含数据库密码、密钥
  • .git/config:Git 仓库信息
  • wp-config.php:WordPress 配置
  • config.phpdatabase.yml

搜索敏感文件关键词

1
findstr /R /I "\.env \.git config phpinfo \.bak" nginx_access.log | findstr /V /I "admin"

发现 IP 103.216.40.7700:5601:00 之间,对 /.env 发出了几十次请求,全部返回 404:

虽然都是404,但这种反复扫同一个路径的行为,就是典型的敏感文件探测

.env 文件是 PHP/Node.js 等项目常用的环境配置文件,一旦泄露,里面可能有数据库账号密码、第三方 API 密钥等,危害极大。

flag{/.env}

题目四:后台登录成功使用的用户名

opencart_error.log是通用应用日志

包含三种级别的记录

1
2
3
Warning: Login failed...     ← 警告(登录失败)
Info: Admin login success... ← 普通信息(登录成功)
Error: xxx ← 真正的错误

开发者偷懒,把所有级别的日志都写进同一个文件,只是用 Warning / Info / Error 前缀区分。所以文件叫error.log,里面却有成功信息。

1
2
3
findstr /I "login" opencart_error.log
2026-02-18 00:41:12 - Warning: Login failed for username 'admin' from 103.216.40.77
2026-02-18 01:08:01 - Info: Admin login success for username 'admin' from 45.133.12.77

日志有不同的层次,打个比方:

层次 日志文件 记录什么
网络层 nginx_access.log 谁发了什么 HTTP 请求,返回什么状态码
应用层 opencart_error.log 网站程序自己记的:登录成功/失败、订单创建等业务事件
数据库层 mysql_general.log 执行了哪些 SQL 语句
系统层 auth.log SSH 登录、系统用户认证

nginx 只知道”有人 POST 了登录接口,返回了 200”,但它不知道账号密码对不对,因为验证密码是 OpenCart 程序自己做的事。

所以要知道用户名是什么、登录有没有真正成功,必须去问 OpenCart 自己写的日志,也就是 opencart_error.log

1
2
3
findstr /I "login" opencart_error.log
2026-02-18 00:41:12 - Warning: Login failed for username 'admin' from 103.216.40.77
2026-02-18 01:08:01 - Info: Admin login success for username 'admin' from 45.133.12.77

日志里明确写了username='admin'

flag{admin}

题目五:数据库中被查询的订单数据表名称

由题3可以了解到完成后台登录成功的攻击IP首次访问后台登录入口的时间为01:28:52

因此我们在mysql数据库表里搜索01:28

1
findstr "01:28" mysql_general.log
1
2
3
4
5
2026-02-18T01:28:12     Connect oc_user@localhost on opencart
2026-02-18T01:28:13 Query SELECT order_id,total FROM oc_order ORDER BY date_added DESC LIMIT 5;
2026-02-18T01:28:14 Query SELECT product_id FROM oc_product WHERE status=1 LIMIT 20;
2026-02-18T01:28:20 Query SELECT product_id,model,price FROM oc_product ORDER BY date_added DESC LIMIT 5;
2026-02-18T01:28:25 Quit

01:28:12有一个新的数据库连接

紧接着是一系列查询,01:28:25断开连接

1
2
3
4
5
6
SELECT order_id, total FROM oc_order ORDER BY date_added DESC LIMIT 5;
查order_id和total这两列
从oc_order这张表里查
按添加时间倒序添加(最新的在前面)
只取前5条
从订单表中,取最新的5条订单,只看id和总金额

像这样 比如说oc_order这张表可能长这样

1
2
3
4
5
6
7
┌──────────┬────────┬─────────────────────┬────────┐
│ order_id │ total │ date_added │ status │
├──────────┼────────┼─────────────────────┼────────┤
│ 10023 │ 299.00 │ 2026-02-17 23:11:00 │ paid │
│ 10022 │ 88.50 │ 2026-02-17 20:33:00 │ paid │
│ 10021 │ 450.00 │ 2026-02-17 18:05:00 │ pending│
└──────────┴────────┴─────────────────────┴────────┘

SQL查出来的结果就是这张表里order_idtotal的数据

1
2
3
4
5
SELECT order_id, total FROM oc_order        
-- order_id=订单号, total=总金额 → 订单表

SELECT product_id, model, price FROM oc_product
-- product_id=商品ID, price=价格 → 商品表

所以订单表就是oc_order而商品表就是oc_product

flag{oc_order}

题目六:数据库中被查询的商品数据表名称

同理 商品表就是oc_product

flag{oc_product}

时间线复盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00:41  103.216.40.77  尝试登录后台 admin 账号 → 失败

00:56 103.216.40.77 开始疯狂扫描 /.env 文件(侦察阶段)
同时探测 /admin/ 入口

01:07 39.98.12.44 对 /admin 后台发起暴力破解(几十次 POST)

01:28 45.133.12.77 ← 真正的攻击者出现
:52 POST 登录成功(用户名 admin)
:54 GET Dashboard,拿到真实 user_token
:59 通过文件管理器上传 Webshell 文件
(伪装成图片 cache_218.jpg,实为 .cache_hidden_218.php)

01:49 45.133.12.77
:20 GET 访问 Webshell → 成功响应(200)
:27 POST 向 Webshell 发送命令执行指令(getshell)
上一页
2026-08-22 20:25:00
下一页