Flare-on 11


Flare-on 11

参考资料:

https://clarkiv.dev/posts/Flareon11

Flare-On 11 Challenge Solutions

frog

运行可执行文件,要求接触到雕像才给flag

看源码,flag生成:

1
2
3
4
def GenerateFlagText(x, y):
key = x + y*20
encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf"
return ''.join([chr(ord(c) ^ key) for c in encoded])

这个xy在这里被调用

image-20250318112237556

image-20250318112247537

1
victory_tile = pygame.Vector2(10, 10)

exp:

1
2
3
4
5
6
def GenerateFlagText(x, y):
key = x + y*20
encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf"
return ''.join([chr(ord(c) ^ key) for c in encoded])

print(GenerateFlagText(10, 10))

当时候做题是直接把砖块全删掉

image-20250318112711089

checksum

运行

image-20250318113755368

ida字符串搜索,定位main_main函数

image-20250318113313203

程序流程为 运算、检验checksum,如果某一步出问题,都会跳到main_b

进入main_a:

image-20250318120403708

逆向写程序出flag:

1
2
3
4
5
6
7
8
9
10
import base64

enflaged = 'cQoFRQErX1YAVw1zVQdFUSxfAQNRBXUNAxBSe15QCVRVJ1pQEwd/WFBUAlElCFBFUnlaB1ULByRdBEFdfVtWVA=='
enflag = base64.b64decode(enflaged)
key = 'FlareOn2024'
flag = ''
for i in range(len(enflag)):
flag += chr(enflag[i] ^ ord(key[i % len(key)]))
print(flag)
# 7fd7dd1d0e959f74c133c13abb740b9faa61ab06bd0ecd177645e93b1e3825dd

写入checksum,文件发现FLAG文件

image-20250318122208730

Aray

Yara规则匹配

(挠头)很多都是没什么用的信息

把hash加密过的短字符串先恢复出来,几乎全出来了(手工活)

1RuleADayK33p$Malw4r3Aw4y@flare-on.com

Meme Maker 3000

JS 逆向 和 混淆JavaScript Deobfuscator

去混淆后,定位可疑代码:

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
function a0k() {
const a = a0g.alt.split('/').pop()
if (a !== Object.keys(a0e)[5]) {
return
}
const b = a0l.textContent,
c = a0m.textContent,
d = a0n.textContent
if (
a0c.indexOf(b) == 14 &&
a0c.indexOf(c) == a0c.length - 1 &&
a0c.indexOf(d) == 22
) {
var e = new Date().getTime()
while (new Date().getTime() < e + 3000) {}
var f =
d[3] +
'h' +
a[10] +
b[2] +
a[3] +
c[5] +
c[c.length - 1] +
'5' +
a[3] +
'4' +
a[3] +
c[2] +
c[4] +
c[3] +
'3' +
d[2] +
a[3] +
'j4' +
a0c[1][2] +
d[4] +
'5' +
c[2] +
d[5] +
'1' +
c[11] +
'7' +
a0c[21][1] +
b.replace(' ', '-') +
a[11] +
a0c[4].substring(12, 15)
f = f.toLowerCase()
alert(atob('Q29uZ3JhdHVsYXRpb25zISBIZXJlIHlvdSBnbzog') + f)
}
}

image-20250318132223649

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
const a0c = [
'When you find a buffer overflow in legacy code',
'Reverse Engineer',
'When you decompile the obfuscated code and it makes perfect sense',
'Me after a week of reverse engineering',
'When your decompiler crashes',
"It's not a bug, it'a a feature",
"Security 'Expert'",
'AI',
"That's great, but can you hack it?",
'When your code compiles for the first time',
"If it ain't broke, break it",
"Reading someone else's code",
'EDR',
'This is fine',
'FLARE On',
"It's always DNS",
'strings.exe',
"Don't click on that.",
'When you find the perfect 0-day exploit',
'Security through obscurity',
'Instant Coffee',
'H@x0r',
'Malware',
'$1,000,000',
'IDA Pro',
'Security Expert',
];

const a = 'boy_friend0.jpg';
const b = 'FLARE On';
const c = 'Security Expert';
const d = 'Malware';

var f =d[3] +'h' +a[10] +b[2] +a[3] +c[5] +c[c.length - 1] +'5' +a[3] +'4' +a[3] +c[2] +c[4] +c[3] +'3' +d[2] +a[3] +'j4' +a0c[1][2] +d[4] +'5' +c[2] +d[5] +'1' +c[11] +'7' +a0c[21][1] +b.replace(' ', '-') +a[11] +a0c[4].substring(12, 15);
f = f.toLowerCase();

alert(atob('Q29uZ3JhdHVsYXRpb25zISBIZXJlIHlvdSBnbzog') + f);

// wh0a_it5_4_cru3l_j4va5cr1p7@flare-on.com

sshd

Crashdump

蒽,从时间来看,找到了sshd.core文件

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop/flareon-sshd/ssh_container]
└─$ find . -type f -printf '%T@ %p\n' | sort -nr | head
1726088159.0000000000 ./root/flag.txt
1725917676.0000000000 ./var/lib/systemd/coredump/sshd.core.93794.0.0.11.1725917676
1725917673.0000000000 ./usr/lib/x86_64-linux-gnu/liblzma.so.5.4.1
1725916919.0000000000 ./var/log/dpkg.log
1725916919.0000000000 ./var/log/apt/term.log
1725916919.0000000000 ./var/log/apt/history.log
1725916919.0000000000 ./var/lib/dpkg/status
1725916919.0000000000 ./etc/ssl/certs/ca-certificates.crt
1725916918.0000000000 ./var/cache/ldconfig/aux-cache
1725916918.0000000000 ./usr/share/mime/XMLnamespaces

因为题目文件是拖入之前存在的虚拟机里面,所以需要设置sshd的使用路径

  1. 启动 GDB:
1
gdb
  1. 设置 sysroot 为文件夹路径:
1
(gdb) set sysroot /path/to/your/folder
  1. 加载可执行文件和核心转储文件:
1
2
(gdb) file /path/to/your/folder/usr/sbin/sshd
(gdb) core /path/to/your/folder/var/lib/systemd/coredump/sshd.core.93794.0.0.11.1725917676

image-20250318191344090

接着跟踪堆栈,bt查看崩溃时调用栈

image-20250318191601116

提到./usr/lib/x86_64-linux-gnu/liblzma.so.5和./usr/lib/x86_64-linux-gnu/liblzma.so.5.4.1,去看看

XZ Utils Background

sshd and liblzma, which lead us to the famous CVE-2024-3094

image-20250318192819051

拖liblzma.so.5.4.1进入IDA,找到RSA_public_decrypt被调用处,且*a2=0xC5407A48时被调用

image-20250318200928837

image-20250318195627669

Decrypt Shellcode

这里对这个解密算法理解的很好:FlareOn11: Challenge 5 - sshd ~ Flareon11:挑战5 -SSHD

sub_93F0进入,image-20250318201657818

key是刚刚的a2+1到a2+9前

image-20250318201743331

943df638a81813e2de6318a507f9a0ba2dbb8a7ba63666d08d11a65ec914d66f

nonce是a2+9到a2+12:f236839f4dcd711a52862955

0x23960—+0x0F96—–0x248F6

image-20250318205223681

shellcode分析

socket 传入ip:port

image-20250318212713292

image-20250318212627179

chacha20初始化

ChaCha key

因为IDA很多没有分析出来,直接看汇编,这里传参+调用syscal

可以通过系统调用号判断做了什么,0x2d是接受、0x02是打开、0x00是读入

shellcode执行了一些shellcode,0x20\0x0c\0x04,分别是chacha20的key、nonce、长度、文件路径

image-20250318213429414

发送文件

image-20250318214439307

恢复文件

在字符串中,此为K而非k,所以是非标准chacha20

image-20250318210058221

返回转储文件core

这里根据“路径”,继续找可能存放的位置

The file path is null terminated and is prepended by a 4-byte value.

image-20250318215417980

恰好这里有四个缓冲区

image-20250318215621842

image-20250318215834808

看起来D18–D3D是密文部分

但这里不是普通的chacha20,自定义算法,

还原算法1

比较牛逼一点直接写自定义chacha20,但我不会,先放这边:

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
from typing import List, Tuple
import struct

def rotl32(v: int, c: int) -> int:
"""Rotate left operation"""
return ((v << c) & 0xffffffff) | (v >> (32 - c))

def quarter_round(state: List[int], a: int, b: int, c: int, d: int) -> None:
"""ChaCha20 quarter round operation"""
state[a] = (state[a] + state[b]) & 0xffffffff
state[d] ^= state[a]
state[d] = rotl32(state[d], 16)

state[c] = (state[c] + state[d]) & 0xffffffff
state[b] ^= state[c]
state[b] = rotl32(state[b], 12)

state[a] = (state[a] + state[b]) & 0xffffffff
state[d] ^= state[a]
state[d] = rotl32(state[d], 8)

state[c] = (state[c] + state[d]) & 0xffffffff
state[b] ^= state[c]
state[b] = rotl32(state[b], 7)

def chacha20_block(key: bytes, counter: int, nonce: bytes, sigma: bytes) -> bytes:
"""Generate a ChaCha20 block"""
state = [0] * 16

state[0:4] = struct.unpack('<IIII', sigma)
state[4:12] = struct.unpack('<IIIIIIII', key)
state[12] = counter
state[13:16] = struct.unpack('<III', nonce)

working_state = state.copy()

for _ in range(10):
quarter_round(working_state, 0, 4, 8, 12)
quarter_round(working_state, 1, 5, 9, 13)
quarter_round(working_state, 2, 6, 10, 14)
quarter_round(working_state, 3, 7, 11, 15)
quarter_round(working_state, 0, 5, 10, 15)
quarter_round(working_state, 1, 6, 11, 12)
quarter_round(working_state, 2, 7, 8, 13)
quarter_round(working_state, 3, 4, 9, 14)

working_state = [(working_state[i] + state[i]) & 0xffffffff for i in range(16)]

return b''.join(struct.pack('<I', x) for x in working_state)

def chacha20_crypt(ciphertext: bytes, key: bytes, nonce: bytes, counter: int = 0, sigma: bytes = b'expand 32-byte k') -> bytes:
plaintext = bytearray()
for i in range(0, len(ciphertext), 64):
keystream = chacha20_block(key, counter + (i // 64), nonce, sigma)
chunk = ciphertext[i:i + 64]
plaintext.extend(x ^ y for x, y in zip(chunk, keystream[:len(chunk)]))

return bytes(plaintext)

def main():
# key (32 bytes)
key = bytes.fromhex("8D EC 91 12 EB 76 0E DA 7C 7D 87 A4 43 27 1C 35 D9 E0 CB 87 89 93 B4 D9 04 AE F9 34 FA 21 66 D7".replace(" ", ""))
# nonce (12 bytes)
nonce = bytes.fromhex("11 11 11 11 11 11 11 11 11 11 11 11".replace(" ", ""))
# Custom sigma constant (capital K)
custom_sigma = b'expand 32-byte K'

encrypted_data = bytes.fromhex("A9 F6 34 08 42 2A 9E 1C 0C 03 A8 08 94 70 BB 8D AA DC 6D 7B 24 FF 7F 24 7C DA 83 9E 92 F7 07 1D 02 63 90 2E C1 58".replace(" ", ""))

decrypted = chacha20_crypt(encrypted_data, key, nonce, 0, custom_sigma)

print(decrypted.decode(errors="ignore"))

if __name__ == "__main__":
main()
# supp1y_cha1n_sund4y@flare-on.com
#XmU
还原算法2:

Flare-On CTF 11-writeup | 堇姬 Naup’s Blog

chacha20實作參考這個
https://github.com/marcizhu/ChaCha20/

改這個庫裡面的 #define CHACHA20_CONSTANT "expand 32-byte K"

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
#define CHACHA20_IMPLEMENTATION
#include "ChaCha20.h"
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

int main()
{
static key256_t key = {0x8d, 0xec, 0x91, 0x12, 0xeb, 0x76, 0x0e, 0xda,
0x7c, 0x7d, 0x87, 0xa4, 0x43, 0x27, 0x1c, 0x35,
0xd9, 0xe0, 0xcb, 0x87, 0x89, 0x93, 0xb4, 0xd9,
0x04, 0xae, 0xf9, 0x34, 0xfa, 0x21, 0x66, 0xd7};

static nonce96_t nonce = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11};

uint32_t count = 0;

static uint8_t data[] = {0xa9, 0xf6, 0x34, 0x08, 0x42, 0x2a, 0x9e, 0x1c,
0x0c, 0x03, 0xa8, 0x08, 0x94, 0x70, 0xbb, 0x8d,
0xaa, 0xdc, 0x6d, 0x7b, 0x24, 0xff, 0x7f, 0x24,
0x7c, 0xda, 0x83, 0x9e, 0x92, 0xf7, 0x07, 0x1d};

ChaCha20_Ctx ctx;
ChaCha20_init(&ctx, key, nonce, count);
ChaCha20_xor(&ctx, data, sizeof(data));
for (size_t i = 0; i < sizeof data; i++) {
printf("%c",data[i]);
}

// The array 'data' is now encrypted (or decrypted if it
// was already encrypted)
}

image-20250318222408535

学到一种,写服务器法

修改刚刚的ip为127.0.0.1

image-20250318220846198

擦,没有复现成功

bloke2

6: bloke2

https://clarkiv.dev/posts/Flareon11

data_mgr.v

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
module data_mgr #(
parameter W = 32 // 参数 W,定义数据宽度,默认值为 32
) (
input clk, // 时钟信号
input rst, // 复位信号,高电平有效

input [7:0] data_in, // 8 位输入数据
input dv_in, // 数据有效信号,指示 data_in 是否有效
output drdy_out, // 数据准备好信号,指示模块是否可以接收新数据
input start, // 开始信号,用于初始化模块
input finish, // 结束信号,指示输入数据流结束

output msg_strobe, // 消息选通信号,用于指示消息处理完成
output [(W*16)-1:0] m_out, // 输出消息数据,宽度为 W*16
output [(W*2)-1:0] t_out, // 输出时间戳数据,宽度为 W*2
output f_out, // 完成信号,指示数据处理完成

input [(W*8)-1:0] h_in, // 外部输入的哈希值,宽度为 W*8
input h_rdy, // 哈希值有效信号,指示 h_in 是否有效

output [7:0] data_out, // 8 位输出数据
output dv_out, // 数据有效信号,指示 data_out 是否有效
output data_end // 数据结束信号,指示输出数据流结束
);

// 定义局部参数
localparam MSG_BITS = W * 16; // 消息数据的总位数
reg [MSG_BITS-1:0] m; // 存储输入数据的寄存器
assign m_out = m; // 将 m 的值输出到 m_out

reg [W*2-1:0] t; // 存储时间戳的寄存器
assign t_out = {t[0 +: W], t[W +: W]}; // 将 t 的值分为两部分输出到 t_out

reg f; // 完成标志寄存器
assign f_out = f; // 将 f 的值输出到 f_out

reg tst; // 测试标志寄存器,用于控制哈希值的处理

// 定义计数器宽度
localparam CNT_BITS = $clog2(W * 16 / 8); // 计数器宽度,计算方式为 log2(W*16/8)
reg [CNT_BITS-1:0] cnt; // 计数器,用于跟踪输入数据的字节位置
wire [CNT_BITS:0] next_cnt = cnt + 1; // 下一个计数器的值
wire carry = next_cnt[CNT_BITS]; // 计数器进位信号

// 生成 msg_strobe 信号
assign msg_strobe = (carry & dv_in) | (finish & ~f & ~start);

// 主逻辑:处理输入数据
always @(posedge clk) begin
if (rst | start) begin // 复位或启动时初始化
m <= {MSG_BITS{1'b0}}; // 清零消息寄存器
cnt <= {CNT_BITS{1'b0}}; // 清零计数器
t <= {(W*2){1'b0}}; // 清零时间戳寄存器
f <= 1'b0; // 清零完成标志
tst <= finish; // 更新测试标志
end else begin
if (dv_in) begin // 如果输入数据有效
m[((W - cnt) * 8) +: 8] <= data_in; // 将 data_in 存储到 m 的适当位置
cnt <= next_cnt[CNT_BITS-1:0]; // 更新计数器
t <= t + 1; // 更新时间戳
f <= finish; // 更新完成标志
end else if (finish) begin // 如果结束信号有效
f <= 1'b1; // 设置完成标志
end
end
end

// 定义测试值
localparam TEST_VAL = 512'h3c9cf0addf2e45ef548b011f736cc99144bdfee0d69df4090c8a39c520e18ec3bdc1277aad1706f756affca41178dac066e4beb8ab7dd2d1402c4d624aaabe40;

// 定义哈希值寄存器和输出计数器
reg [(W*8)-1:0] h; // 存储哈希值的寄存器
reg [$clog2(W):0] out_cnt; // 输出计数器,用于跟踪输出数据的字节位置
assign data_out = h[7:0]; // 输出 h 的最低 8 位
assign dv_out = (out_cnt != 0); // 如果 out_cnt 不为零,则 dv_out 有效
assign data_end = (out_cnt == 1); // 如果 out_cnt 为 1,则 data_end 有效

// 主逻辑:处理输出数据
always @(posedge clk) begin
if (rst) begin // 复位时初始化
out_cnt <= 0; // 清零输出计数器
end else begin
if (h_rdy) begin // 如果哈希值有效
out_cnt <= W; // 设置输出计数器为 W
h <= h_in ^ (TEST_VAL & {(W*16){tst}}); // 将 h_in 与 TEST_VAL 异或后存储到 h
end else if (out_cnt != 0) begin // 如果输出计数器不为零
out_cnt <= out_cnt - 1; // 递减输出计数器
h <= {8'b0, h[W*8-1:8]}; // 将 h 右移 8 位
end
end
end
endmodule

在kali中安装iverilog并make tests

仅通过修改代码并将TST行更改为TST <= 1;我们将获得以下输出

image-20250319220144003

或许是一个思路,官方的想法:

根据输入输出的高低电频?

fullspeed

https://0xdf.gitlab.io/flare-on-2024/fullspeed#

Flareon 11 Writeup Part 2 - VNPT Cyber Immunity ~

Has this all been far too easy? Where’s the math? Where’s the science? Where’s the, I don’t know…. cryptography? Well we don’t know about any of that, but here is a little .NET binary to chew on while you discuss career changes with your life coach.

  • Ahead-of-Time (AOT) compiled:

提前(AOT)汇编是一个过程,将程序在执行机器代码之前被编译到机器代码中,而不是在运行时(如在Just-In-time(JIT)汇编)中进行解释或编译。在AOT中,在构建过程中将源代码(或字节码之类的中间表示)编译为目标平台的本机机器代码。这仅表示用C#编写的.NET程序已编译为本机机器代码。

附件是一个小流量包+64位exe程序

流量包

image-20250320134145566

看程序字符串,BouncyCastle加密的内容

image-20250320095626931

还调用了.NET核心应用程序及其特定版本

FLIRT

第一步是快速库标识(Fast Library Identification and Recognition Technology)


文章作者: W3nL0u
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 W3nL0u !
  目录