一切尽在不言中。
初步仿真 服务器两张网卡,对应在vmware中创建两张网卡,配置好后即可rdp连接,便于后面输命令
看服务,FICWEB和PostgreSQL是自动启动的,contained和Docker是手动
计划任务中有Mount VHDX at Startup,会把ROOT.vhdx挂载到T,并在T下创建root和state连个目录。同时启动containerd
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command { Import-Module Hyper-V $VHDPath = 'C:\Users\Administrator\Documents\ROOT.vhdx' $DriveLetter = 'T' $DriveRoot = $DriveLetter + ':\' Mount-VHD -Path $VHDPath -ErrorAction SilentlyContinue Start-Sleep -Seconds 3 $disk = Get-VHD -Path $VHDPath | Get-Disk if ($disk .IsOffline) { Set-Disk -Number $disk .Number -IsOffline $false } if ($disk .IsReadOnly) { Set-Disk -Number $disk .Number -IsReadOnly $false } $partition = $disk | Get-Partition | Where-Object { $_ .Type -ne 'Reserved' } | Select-Object -First 1 if ($partition .AccessPaths -notcontains $DriveRoot ) { Add-PartitionAccessPath ` -DiskNumber $disk .Number ` -PartitionNumber $partition .PartitionNumber ` -AccessPath $DriveRoot } for ($i = 0 ; $i -lt 60 ; $i ++) { if (Test-Path $DriveRoot ) { break } Start-Sleep -Seconds 1 } if (-not (Test-Path $DriveRoot )) { throw 'T drive not available after mounting VHDX' } New-Item -ItemType Directory -Force ($DriveRoot + 'root' ) | Out-Null New-Item -ItemType Directory -Force ($DriveRoot + 'state' ) | Out-Null Start-Service containerd }
计划任务是开机自启动的,启动后可以直接看见有:
nerdctl ps -a可以看到当前启动的容器,postgres已启动
然后启动网站,直接运行exe启动会报错,用sc.exe可以启动
PS C:\FIC_WEB> sc.exe start FicWeb SERVICE_NAME: FicWeb TYPE : 10 WIN32_OWN_PROCESS STATE : 2 START_PENDING (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x7d0 PID : 5964 FLAGS : PS C:\FIC_WEB> sc.exe query FicWeb SERVICE_NAME: FicWeb TYPE : 10 WIN32_OWN_PROCESS STATE : 4 RUNNING (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
然后直接用ip访问不到,要改hosts并配置本地dns才能启动 先改hosts 172.28.0.100 admin.ficads.site 172.28.0.100 mtls.ficads.site 172.27.0.100 ficads.site
然后会发现后台还是连不上,是因为这里走的是http3,对应的传输层是QUIC,基于UDP。
用这个方式启动,强制这个域名走QUIC:
$chrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe' $profile = "$env:TEMP \fic-admin-http3-profile" Start-Process -FilePath $chrome -ArgumentList @ ( '--user-data-dir=' + $profile , '--origin-to-force-quic-on=admin.ficads.site:443' , '--host-resolver-rules="MAP admin.ficads.site 172.28.0.100, MAP mtls.ficads.site 172.28.0.100"' , 'https://admin.ficads.site/' )
登录后面再说
题目部分 1. 分析服务器检材,系统分区的文件系统类型是? 【参考格式:FAT32】
ReFS
fsutil fsinfo volumeInfo C:
2. 分析服务器检材,系统分区的文件系统版本是多少? (格式:1.23)
3.14
fsutil fsinfo refsInfo C:
3. 分析服务器检材,系统的 SKU 编号(OperatingSystemSKU)是什么? 【参考格式:101】
407
Get-CimInstance Win32_OperatingSystem | select Caption,OperatingSystemSKU 这个编号的意思是windows操作系统版本的内部编号
4. 服务器中的 WinRM HTTP 服务的端口是多少? (格式:65535)
59508
PS C:\FIC_WEB> winrm enumerate winrm/config/listener Listener Address = IP:172.28 .0.100 Transport = HTTP Port = 59508 Hostname Enabled = true URLPrefix = wsman CertificateThumbprint ListeningOn = 172.28 .0.100
不能单纯看winrm get config,这个只会打印默认的端口,这里需要查看实际的监听器端口
5. 最后一次通过 WinRM 远程访问的北京时间是? (格式:1970-01-01 00:11:22)
2026-05-17 16:38:06
查看这个日志Microsoft-Windows-WinRM/Operational
Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational' | Where-Object { $_ .Id -eq 91 } | Sort-Object TimeCreated -Descending | Select-Object -First 1 TimeCreated, Id, Message
比赛的时候没有ai,写不了这么复杂的powershell命令,可以打印日志,找出最后一条连接日志Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational'
或者看evtx文件也可以,连接事件对应的id为91,request handling/分区6/Windows/System32/winevt/Logs/Microsoft-Windows-WinRM%4Operational.evtx
最上面一条就是
6. 服务器除了允许上一题登录记录的 IP 地址远程登录,还允许哪个 IP 地址远程管理? (格式:1.2.3.4)
117.184.20.123
先看看上面一题登录的ip是多少,evtx文件里面可以找到,218.80.0.18
然后这个肯定和防火墙规则有关,可以直接看SYSTEM文件,找到59508端口相关的防火墙配置 下面两条就是RemoteAddress,218.80.0.18和117.184.20.123
或者直接输入命令过滤查看
Get-NetFirewallPortFilter |Where-Object LocalPort -eq 59508 |Get-NetFirewallRule |ForEach-Object { $_ | Get-NetFirewallAddressFilter | Select-Object LocalAddress, RemoteAddress }
那么这题就是117.184.20.123
7. 主机 secrets 目录中存放有一批密码文档,找到存在历史快照版本的文件,该文件历史版本中存放的密码是什么? (格式:Abc123!@#)
pMLCzXOd+<.&.pUw
这两题考点是ReFS文件系统文件流的快照,有关ReFS文件系统的流,要用refsutil streamsnapshot工具来查看。这个结构和NTFS ADS流蛮像的,只不过Refs的流一般更偏向于文件快照,ADS流更偏向附加数据
查看流
Get-ChildItem . -File -Recurse | ForEach-Object { $f = $_ Get-Item -LiteralPath $f .FullName -Stream * | Where-Object Stream -like '*:$SNAPSHOT' | Select-Object @ {n='File' ;e={$f .FullName}}, Stream, Length }
发现secret012存在一个快照
导出
$srcFile = 'C:\Users\Administrator\secrets\secret_012.docx' $streamName = 'snapshot_sec' $dstFile = 'C:\Users\Administrator\secrets\secret_012.snapshot.docx' $data = Get-Content -LiteralPath $srcFile -Stream $streamName -Encoding Byte -Raw [System.IO.File]::WriteAllBytes($dstFile, $data)
利用给的管理员密码smb上去Copy-Item即可
Enable-NetFirewallRule -Name FPS-SMB-In-TCP cmdkey /add:172.28 .0.100 /user:Administrator /pass:F1cWinC@reGet-ChildItem '\\172.28.0.100\c$\Users\Administrator\secrets' Copy-Item '\\172.28.0.100\c$\Users\Administrator\secrets\secret_012.snapshot.docx' 'secret_012.snapshot.docx' -Force
8. 嫌疑人称,服务器中曾安装有 Linux 虚拟机用于下载合同文件,其中存有一份加密的合同文件,找到这个文件,计算文件的 MD5 值。 (格式:d41d8cd98f00b204e9800998ecf8427e)
921A242639D1A13F6D6BFA47D9EB34D5
题目说的linux虚拟机肯定是fedora-coreos-44.20260419.3.1-hyperv.x86_64.vhdx这个hyperv虚拟机,但是同时,可以看到很多avhd快照文件。对于hyperv快照来说,生成的是差分盘,快照中只保留修改的内容。
这个题要恢复,得还原出每个快照的状态。
查看所有磁盘的父盘信息Get-VHD -Path * | Format-List Path, ParentPath
整理一下,目标的合同文件有可能是在中间节点出现的,所以最好恢复所有的状态,应该一共是7个状态,一个一个看。
根盘 vhdx └── C03E3EEC ├── 9F02D431 │ └── 6994585D └── FF7E62B2 └── D451E349 └── F5DEFE8C
一个一个还原就行
Copy-Item C:\Users\Administrator\VM C:\Users\Administrator\VM_C03E3EEC -Recurse cd C:\Users\Administrator\VM_C03E3EECMerge-VHD -Path .\fedora-coreos-44 .20260419.3 .1 -hyperv .x86_64_C03E3EEC-C39B-4BDA-8F2E-8A69C216E73F .avhdx ` -DestinationPath .\fedora-coreos-44 .20260419.3 .1 -hyperv .x86_64.vhdxCopy-Item .\fedora-coreos-44 .20260419.3 .1 -hyperv .x86_64.vhdx C:\Users\Administrator\C03E3EEC_state.vhdx
翻一下文件系统,或者直接搜合同,在这两个状态中找到加密的文件
计算md5即可,921A242639D1A13F6D6BFA47D9EB34D5
9. 管理后台页面的 TLS 证书的 SHA1 指纹是什么? (格式:da39a3ee5e6b4b0d3255bfef95601890afd80709)
248424A5234D539CBF0425751A437ADCC106D835
看/分区6/FIC_WEB/appsettings.json,有网站证书相关的配置 刚刚我们仿真的网站也可以看到
这张证书对应的sha1是248424A5234D539CBF0425751A437ADCC106D835
10. 广告页面提供服务的网卡 MAC 地址是什么? (格式:11:22:33:aa:bb:cc)
00:15:5d:19:96:2c
还是网站配置文件,publiclistenip监听的是172.27.0.1这张,也就是广告页面。实际上ipconfig看不见这个ip,但是前面网段是一样的,猜测大概率对应的是172.27.0.100这张网卡。 逆向网站exe程序应该也行,但是ida打开全是修饰符……
11. 管理员在北京时间 2026-05-01 12:23:21 通过聊天工具向他人发送过一个验证码,计算管理员在这个时间登录管理后台需要使用的验证码是否与聊天记录中的验证码相同,这个时间的验证码是? (格式:12345678)
65625
https://admin.ficads.site/login这个后台这里,要求TOTP动态码和客户端证书,那么这道题目应该考察的就是TOTP的还原。
首先要找key,从下面第15题可以得到数据库密码yiZvGzBS9FYxrw42J17LNUK5tlDo(详细过程见15题),然后本机可以直接连接的 admin_top_secrets表中找到对应的secret为JBSWY3DPEHPK3PXP,这个secret是可以长时间复用的,数据库中这个key生成的时间是4月20日 比较坑的是,这个网站的2FA认证使用的不是默认的6位验证码,而是5位。这点需要逆向网站程序看出。
网站是.NET,用dnSpy或者ILSpy可以直接反编译,很好定位TOTP的逻辑,在TotpService,规定了验证码长度为5
然后根据题目的时间计算TOTP
import pyotpimport datetimeimport pytz SECRET = "JBSWY3DPEHPK3PXP" totp = pyotp.TOTP(SECRET, digits=5 ) beijing_tz = pytz.timezone('Asia/Shanghai' ) target_time = beijing_tz.localize(datetime.datetime(2026 , 5 , 1 , 12 , 23 , 21 )) timestamp = target_time.timestamp()print (totp.at(timestamp))
12. 从现场扣押的证书文件中寻找网站管理员登录后台使用的证书,正确的证书文件的 SHA1 哈希是什么? (格式:da39a3ee5e6b4b0d3255bfef95601890afd80709)
c48b687f0ffca44b626f1794bb6cb81ca8c8df9f
还是在pg数据库中,数据库的admin_client_certificates记录了证书的信息 证书指纹为F628659F0D2B175736B049FAC1D0659AF81033B82DB071CF4D201FF70F4DCA8B,在对应的附件文件夹找到
from pathlib import Pathimport hashlibfrom cryptography import x509from cryptography.hazmat.backends import default_backend target_fingerprint = "F628659F0D2B175736B049FAC1D0659AF81033B82DB071CF4D201FF70F4DCA8B" search_dir = Path(r"E:\Admin\Desktop\附件3" )for cert_file in search_dir.glob("*.cer" ): data = cert_file.read_bytes() sha256 = hashlib.sha256(data).hexdigest().upper() sha1 = hashlib.sha1(data).hexdigest().upper() if sha256 == target_fingerprint: cert = x509.load_der_x509_certificate(data, default_backend()) print ("找到证书:" , cert_file) print ("SHA256 :" , sha256) print ("SHA1 :" , sha1.lower())
找到对应的证书candidate-17.cer,直接打开也能看到,windows默认显示的就是sha1指纹
13. 使用证书文件登录推广管理网站后台,管理员登录后网站右上角的 flag 是什么? (格式:flag{abc_123_!@#})
flag{fic_mtls_2a10229ccc830e737cb54993}
登录需要使用证书+TOTP
先计算当前时间的TOTP验证码,注意服务器时间要和本机时间一样
import pyotpimport time SECRET = "JBSWY3DPEHPK3PXP" totp = pyotp.TOTP(SECRET, digits=5 )print (totp.now())
证书没找到私钥,给的附件只有公钥。留个坑 感觉呼之欲出了。
14. 现在需要从现场扣押的硬件密钥中寻找网站管理员登录后台使用的硬件密钥,网站管理员登录后台使用的硬件密钥中存储的凭据 ID 是什么? (格式:8914d73ee5522094906733031a76bb04e915e9e577ad92e0abe78428bd9dad9c8ac78412819373191547b199fbc0914d)
依旧读取数据库,在admin_webauthn_credentials中有相关的数据 只有5月17日这一条有使用记录,对应的crendential_id为RTw2QDlgJEtCae7XAyFIwQT6EnIJJiElrq6AGONvJerdOtmZKKv5ayP3OgqQjjrc,但是这里格式不一样,实际上应该是base64进行存储,编码转换一下即可 453c36403960244b4269eed7032148c104fa127209262125aeae8018e36f25eadd3ad99928abf96b23f73a0a908e3adc
15. 运行网站数据库的容器中,容器内部操作系统版本是? (格式:25H2)
24H2
postgresql运行在容器中,是一个windows系统,要查看容器内部的操作系统版本,进入容器查看即可。
nerdctl exec -it f4404 pwsh直接这样进入容器没法直接进入shell,会阻塞在容器的启动程序中。创建一个新的临时容器进入
nerdctl run --rm -it --entrypoint pwsh docker.io/innovesys/postgresql-windows:18-nanoserver-ltsc2025 Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" DisplayVersion
拿到OS: Microsoft Windows 10.0.26100,对应的是24H2
16. 推广管理网站使用的 PostgreSQL 数据库版本是多少? (格式:16.1)
18.0
直接看pgdata下的PG_VERSION并不能确定pg数据库的小版本,具体的还是要成功连接数据库后执行命令SELECT version()查看PostgreSQL 18.0 on x86_64-windows, compiled by msvc-19.44.35215, 64-bit
17. 推广管理网站连接 PostgreSQL 数据库使用的密码是什么? (格式:Abc123$%^)
yiZvGzBS9FYxrw42J17LNUK5tlDo
这题实际上是整套题的关键。没有这个数据库密码,后半部分的题目都做不了。 我尝试使用容器启动的环境变量里的密码连接(实际上我比赛的时候也是把这个当做密码了),一直连不上,实际上这个只是初始化数据库的密码,不是实际密码。
然后发现可以mimikatz抓。
cmdkey /add:172.27 .0.100 /user:Administrator /pass:F1cWinC@reCopy-Item 'mimikatz.exe' '\\172.27.0.100\c$\Users\Administrator\mimikatz.exe' -Force
然后会发现传上去之后马上就被删了,也没法运行,是有defender 要先把defender关了
Set-MpPreference -DisableRealtimeMonitoring $true
然后就可以抓了
token::elevate vault::cred
数据库密码为yiZvGzBS9FYxrw42J17LNUK5tlDo
18. 根据推广网站的推荐算法,在北京时间 2026 年 5 月 1 日 12:34:56 时,上海的 IP 地址通过 huajian026.chat-now.vip 访问 https://ficads.site/p/026 的广告页面时,最有可能推荐的应用包名是什么?
com.fic.seed.chatlive095
在数据库里并没有找到对应的数据,找一下会发现实际上这里是调用fic.resolve_promotion_payload_at这个存储过程,一样的参数传回去
select * from fic.resolve_promotion_payload_at('huajian026.chat-now.vip' ,'/p/026' ,'上海' ,timestamptz '2026-05-01 12:34:56+08' );
19. fic.traffic_counters 记录了推广源、APK 和日期维度的访问计数。请找出全量 visit 流量中,累计访问量最高的 host + package_name 组合对应 APK 的 apk_sha256 字段值为? (格式:27AE41E4649B934CA495991B7852B855)
65EA7D9DDB135EF977CA28F7E046D705
traffic_counters表本身并没有host和apk_sha256字段,这里要聚合
apk_targets中有apk_sha256
promotion_sources中有host字段
使用相关的表连接字段(id,source_id)连接即可,累计访问量对应的字段是total_count
20. 数据库中删除过一条创建时间为北京时间 2026-05-13 10:30:00 的 admin 用户的 passkey 信息,请恢复这条记录,credential_id 字段的内容是什么? (格式:ASNFZ4mrze_-3LqYdlQyEA)
PV0o_BzQptB90kmn8I_9VZxw_LKnIJ8IyqSAWOvAtlc
检材中有完整的pg_data数据库文件,我这里把它复制出来在本地做。题目提到的字段是在fic.admin_webauthn_credentials表中,因此恢复wal文件的时候只需要找和这张表有关的记录即可,对应的参数为-R 1663/42852/42994,分别表示表空间、数据库、表名的OID
经过尝试,只有0E这个wal文件可以恢复出数据,其余两个不行
& 'E:\PostgreSQL\18\bin\pg_waldump.exe' ` -p 'E:\Admin\Desktop\pgdata\pg_wal' ` -R 1663/42852/42994 ` 00000001000000000000000E
可以看到几条删除的记录,但是具体的信息看不懂。到这里就借助一下ai了 关键的是这一条记录,FPW代表有完整页镜像
rmgr: Heap len (rec/tot): 59/ 8047, tx: 908, lsn: 0/0E0732F0, prev 0/0E0732B8, desc: DELETE xmax: 908, off: 1, infobits: [KEYS_UPDATED], flags: 0x00, blkref #0: rel 1663/42852/42994 blk 0 FPW
导出
$dir='E:\Admin\Desktop\pgdata\fpw_extract' if (Test-Path $dir) { Remove-Item -LiteralPath $dir -Recurse -Force } New-Item -ItemType Directory -Path $dir | Out-Null & 'E:\PostgreSQL\18\bin\pg_waldump.exe' ` -p 'E:\Admin\Desktop\pgdata\pg_wal' ` -R 1663/42852/42994 ` -B 0 ` --save-fullpage=$dir ` 00000001000000000000000E
然后想办法去读这个镜像的内容,先起一个临时的pg数据库
& 'E:\PostgreSQL\18\bin\pg_ctl.exe' ` -D 'E:\Admin\Desktop\pgdata_recover' ` -l 'E:\Admin\Desktop\pgdata_recover\startup.log' ` -o "-p 55432 -c listen_addresses=127.0.0.1 -c hba_file=E:/Admin/Desktop/pgdata/pg_hba_trust.conf" ` start -w
解析
& 'E:\PostgreSQL\18\bin\psql.exe' -h 127.0.0.1 -p 55432 -U postgres -d ficweb -F '|' -Atc "select lp, lp_flags, t_xmin, t_xmax, t_ctid, encode(t_attrs[1],'escape') as credential_id, encode(t_attrs[2],'hex') as admin_user_id_hex, encode(t_attrs[3],'escape') as factor_kind, (timestamp with time zone '2000-01-01 00:00:00+00' + (((get_byte(t_attrs[10],0)::bigint) + (get_byte(t_attrs[10],1)::bigint<<8) + (get_byte(t_attrs[10],2)::bigint<<16) + (get_byte(t_attrs[10],3)::bigint<<24) + (get_byte(t_attrs[10],4)::bigint<<32) + (get_byte(t_attrs[10],5)::bigint<<40) + (get_byte(t_attrs[10],6)::bigint<<48) + (get_byte(t_attrs[10],7)::bigint<<56))::text || ' microseconds')::interval) as created_at from heap_page_item_attrs(pg_read_binary_file('E:/Admin/Desktop/pgdata/fpw_extract/00000001-00000000-0E0732F0.1663.42852.42994.0_main',0,8192,true), 'fic.admin_webauthn_credentials'::regclass, true) order by lp;"
第一条的时间对上了,答案为PV0o_BzQptB90kmn8I_9VZxw_LKnIJ8IyqSAWOvAtlc
文章作者: Xiaohao
文章链接: https://blog.enxiaohao.cn/posts/Forensics/2026FICFinalWinserverwp/
版权声明:除另有声明外,本博客文章均采用 CC BY-NC-SA 4.0 许可协议。转载请注明原作者与文章出处。