2024年中国工业互联网安全大赛智能家电赛道选拔赛|线上初赛Writeup

背景

中国工业互联网安全大赛自2018年举办以来,吸引了全国各地方、各行业领域的广泛参与,累计近1.8万支队伍、5.6万名选手积极参赛,并成功举办14个省级选拔赛与教育赛道选拔赛,以及基础电信、智能制造、核能、电力、能源等11个行业选拔赛。

随着智能家电的不断迭代升级,网络攻击、数据泄露、恶意软件等安全问题层出不穷,家电安全与每一位消费者密切相关,为此本届工业互联网安全大赛特设智能家电行业分赛道,考题覆盖工业互联网安全、Web安全、协议分析、固件分析、应急响应、逆向分析和密码安全等领域,结合CTF技能操作和物联网家居场景实战,为参赛选手提供技术展示的舞台。

大赛的意义不仅在于全面展示参赛选手在物联网智能家居领域的安全技术能力和知识应用水平,也为了进一步发掘智能家电潜在的安全隐患,提升智能家电行业整体的安全防护水平。
本次大赛分为线上与线下两个阶段,奖金池超10万元,比赛内容以深度解析工业及物联网领域的典型安全事件为主,考察点包括信息收集、应用漏洞利用、系统漏洞利用、Web漏洞利用、暴力破解、固件分析、协议篡改和程序控制等。

组队

无意间看到了这个比赛的报名通知,看了看时间线,约上@kood和@kagi两个大哥组队参加了这次比赛。

所以最终队伍名是:他说起一个长一点的队伍名就挺好的,但我不同意!(最后线下比赛主持人念队名的时候差点没绷住🤣

以下是本次线上比赛所有的Writeup

Web

跨站请求伪造


过滤了如上关键字
gopher打127.0.0.1:8080的xml弱口令即可,密码是admin/admin123456

Reverse

程序逆向分析

简单题,写python可以解出来。核心逻辑是这里:

遇到非a-z的字符,原样输出,且不改变key的进位。

key  = b'flag\x00{\x00this\x00isk\x00e\x00\x00\x00y\x00\x00\x00}f\x00\x00l\x00\x00\x00a\x00\x00g{xthisiskey}'
flag = b'kwam{dGawkqBxuiBlIHNjY3JniCBeZXNzYWjl}'

tmp = []
for x,y in zip(key, flag):
	if y not in range(97, 123):
		print(chr(y), end = '')
		tmp.append(y)
		continue
	print(chr(((y-x)%26) + 97), end = '')
	tmp.append(((y-x)%26) + 97)

tmp = bytes(tmp)
print()

安卓逆向分析

分为两步,第一步,求passwd,第二步,求username。
password算法在java层,可以直接看到:

懒得拆算法了,使用python按位爆破即可。

import base64

def check_key(input_str, l = 1):
    b_arr = [44, 66, 67, 77, 46, 43, 47, 71, 49, 68, 80, 50, 68, 83, 51, 49, 50, 68, 58, 56, 61, 53, 70, 57, 58, 79, 59, 78, 66, 67, 89, 68]
    # print(bytes(b_arr))
    b_arr2 = bytearray(32)
    
    for i in range(len(input_str)):
        b_arr2[i] = (input_str[i] + b_arr[i]) % 101
    
    for i in range(len(input_str)):
        b_arr2[i] = ((b_arr2[i] * 2) - i) % 256
    
    #print(bytes(b_arr2), bytes(b_arr2)[:l] == input_str[:l])
    return bytes(b_arr2)[:l] == input_str[:l]

tmp = []
for i in range(32):
	for x in range(32, 128):
		t = tmp + [x]
		#print(bytes(t))
		if check_key(bytes(t), i + 1):
			print(i, 'solved')
			tmp.append(x)
			break
	print('tmp->', tmp, bytes(tmp))

print(check_key(bytes(tmp), 32))

print(bytes(tmp))

得到password:rGF3ryrCpK4qN1rwvShmduTonEnIba6a
然后从libc中提取n算法,实际上可以直接分析arm的libc包,比较容易看到有rc4init函数。

合理猜测是一个简单的rc4加密,所以可以去找data中的enc内容。发现一个可疑的数据,有且只有一个32位的。


在其他libc中表现一致。

所以直接编写解密代码

from Crypto.Cipher import ARC4

enc = b'\x1B\xB0\xAC\xAD\x4F\x23\x54\x2B\x59\x93\x78\x8F\x39\xF9\x78\xE5\x26\x13\xBD\x8C\xDE\x9D\x9A\xB7\x43\x99\x7E\x11\xEB\x46\xED\xE0'
key = b'rGF3ryrCpK4qN1rwvShmduTonEnIba6a'
cipher = ARC4.new(key)
m=cipher.decrypt(enc)

from hashlib import md5

print(md5(m).hexdigest())

Misc

签到题

base64解密即可

恶意攻击流量

应用系统被植入了恶意后门,并从流量中识别其中的flag

蚁剑Webshell流量分析,掏出写好的自动化分析程序可以秒了,当然也可以手动追踪TCP流来分析23333

var/www/html";echo ZmxhZ3szOTA4NEVFRjJEMjhFOTQxRjUzRTRBMUFBMUZBNjc2Nn0K|base64 -d > ./flag.txt;


可以得到flag{39084EEF2D28E941F53E4A1AA1FA6766}

MQTT协议分析

MQTT协议是一个基于客户端-服务器消息发布/订阅传输协议。MQTT协议具备轻量、简单、开放和易于实现等特点,使它适用范围非常广泛。附件中MQTT报文存在flag

过滤mqtt协议,追踪TCP流,可以看到报文信息。

很显然,需要把msg的字符串全部提取出来,然后会用到passwd_mqtt_6666这个密码。msg全部提取出来后过滤一下,只需要hex的数据,可以得到一个压缩包,压缩包密码是passwd_mqtt_6666

压缩包解密之后可以得到flag{mqtt_flag_8888}

蓝牙数据分析

非预期秒了

最终得到flag{adsjopvap349ls}

ICS

传感器异常

工厂的温度传感器能存储一段时间内的读数,但最近存在异常读取的情况,请把具体Reference Number找出来

过滤modbus.func_code == 24
Reference Number是19934

提交flag{19934}即可

运行组态工程

组态王打开

能看到这几个base64字符串

VxdTY2Ng==
cHkl67tL
c2NjZHBpZH
YfGyj56s

排列组合一下可以得到c2NjZHBpZHVxdTY2Ng==

提交sccdpiduqu666即可

PLC程序运算

Unity Pro打开该文件,发现是梯形图计算。


最后算出来是20416

提交flag{program_unity}即可

Crypto

初涉密码学

n 很小且无别的信息,拿去分解

===============================================================
======= Welcome to YAFU (Yet Another Factoring Utility) =======
=======             bbuhrow@gmail.com
===
   factor(71484438965393396388835335667806052411397994375702758854090697767967524655627)

fac: factoring 71484438965393396388835335667806052411397994375702758854090697767967524655627
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 100 digits
fac: no tune info: using qs/snfs crossover of 75 digits
div: primes less than 10000
fmt: 1000000 iterations
rho: x^2 + 3, starting 1000 iterations on C77
rho: x^2 + 2, starting 1000 iterations on C77
rho: x^2 + 1, starting 1000 iterations on C77
nfs: searching for brent special forms...
nfs: searching for homogeneous cunningham special forms...
nfs: searching for XYYXF special forms...
nfs: searching for direct special forms...
nfs: snfs form detection took 0.188331 seconds
nfs: couldn't find special form
pm1: starting B1 = 150K, B2 = gmp-ecm default on C77
ecm: ECM executable does not exist at ../trosi_ecm_git/ecm
ecm: using internal single threaded ECM...
ecm: 30/30 curves on C77, B1=2k, B2=gmp-ecm default
ecm: ECM executable does not exist at ../trosi_ecm_git/ecm
ecm: using internal single threaded ECM...
ecm: 74/74 curves on C77, B1=11k, B2=gmp-ecm default
ecm: ECM executable does not exist at ../trosi_ecm_git/ecm
ecm: using internal single threaded ECM...
ecm: 149/149 curves on C77, B1=50k, B2=gmp-ecm default, ETA: 0 sec

starting SIQS on c77: 71484438965393396388835335667806052411397994375702758854090697767967524655627

==== sieving in progress (1 thread):   36240 relations needed ====
====           Press ctrl-c to abort and save state           ====
36449 rels found: 16437 full + 20012 from 233943 partial, (2406.79 rels/sec)

building matrix with 36449 columns
SIQS elapsed time = 109.2670 seconds.
Total factoring time = 135.2192 seconds


***factors found***

P41 = 79823191688166851259736970548355545692829
P36 = 895534711824738922785094048763390663

ans = 1

>>
from Crypto.Util.number import *

n = 71484438965393396388835335667806052411397994375702758854090697767967524655627
e = 65537
c = 0x515b50d7407f4f321ddea14d0d99e4134c285ee6b7b92b77f3ed65f32212a529

# yafu
p = 79823191688166851259736970548355545692829
q = 895534711824738922785094048763390663

assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

m = pow(c, d, n)
m = long_to_bytes(m)


def subtract_one_from_ascii(data):
    result = bytearray()
    for byte in data:
        result.append(byte - 1)
    return bytes(result)

def xor_bytes(data, key):
    result = bytearray()
    for byte in data:
        result.append(byte ^ key)
    return bytes(result)

def decode_m(m):
    # step 1: 对 m XOR 12
    m_xor = xor_bytes(m, 12)
    # step 2: 对 res sub 1
    original_x = subtract_one_from_ascii(m_xor)
    return original_x

x = decode_m(m)
print("x:", x)

# Output
# b'\r_&S\x87\xef\x96g\xec\x08\x0bflag{954fbe80adb799}'

比赛结果


算是比较幸运,又可以和队友去营口路海鲜市场玩了😋

作为家电行业的首届国家级网络安全大赛,本届大赛吸引了来自浙江大学、山东大学、中国科学院大学、中国石油大学、哈尔滨工业大学、广州大学、北京邮电大学、西安邮电大学等知名院校,中国移动集团、中国电信集团、中国工商银行等知名集团企业,以及广州为辰、深圳纽创信安等行业特色企业的100+战队。

通过线上赛激烈角逐,30支队伍胜出晋级决赛。决赛设置超10万奖金池,优胜队伍将不但能获得奖金鼓励,也能作为家电行业赛道推荐队伍,直通2024中国工业互联网安全大赛全国总决赛。

国创中心发挥平台优势,搭建网络安全人才技术交流平台,畅通产业、科技、教育、人才良性循环,为加速推动我国制造强国和网络强国建设奠定人才基础。