题目介绍
LaunchDotCom has a new satellite, the Carnac 2.0. What can you do with it from its design doc?
LaunchDotCom公司有一颗新的卫星——Carnac 2.0。卫星地面站正在与其进行遥测数据接收、遥控指令发送。本挑战题要求根据卫星的设计文档,思考如何获取flag。
本挑战题给出了一个链接地址,使用netcat连接到题目给的链接后,会给出进一步提示,如图4-14所示,提示遥测服务运行在另外一个地址上,继续使用netcat连接该地址,与4.2节类似,遥测服务正在发送一系列二进制数据,如图4-15所示,显示为乱码,根据题目信息,参赛者需要研究这颗新卫星的设计文档,从中解码出flag值。
图4-14 goose题目的提示信息
图4-15 连接到遥测服务后,接收到的数据
给出的资料有:
(1)cmd_telemetry_defs.zip,其中文件就是telemetry.xtce,它是一个XTCE文件,告诉我们二进制数据包是如何编码的。
(2)LaunchDotCom_Carnac_2.zip,解压后是文件LaunchDotCom_Carnac_2.pdf,该文件是卫星的设计文档,它详细描述这颗新卫星的信息。
题目编译及测试
本挑战题的代码位于goose目录下,查看challenge、solver目录下的Dockerfile,发现其中用到的是python:3.7-slim,为了加快题目的编译进度,在goose目录下新建一个文件sources.list,内容如下:
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
将sources.list复制到goose、challenge、solver目录下,修改challenge、solver目录下的Dockerfile,在所有的FROM python:3.7-slim下方添加:
ADD sources.list /etc/apt/sources.list
打开终端,进入goose所在目录,执行命令:
sudo make build
使用make test命令进行测试,会顺利通过,其输出信息如图4-16所示。
图4-16 goose挑战题测试输出
题目解析
题目给出卫星设计文档LaunchDotCom_Carnac_2.pdf和遥测遥控数据格式telemetry.xtce,分别对其分析,找出获取flag的相关途径。
(1)LaunchDotCom_Carnac_2.pdf文档。
文档描述了Carnac2.0卫星系统结构,如图4-17所示。
图4-17 Carnac 2.0卫星系统结构
Carnac 2.0卫星系统由以下6个部分组成。
- ADCS:Attitude Determination and Control System(姿态控制系统)。
- Payload Sensors:有效载荷子系统。
- ECS:增强型通信系统(包含Radio1和Radio2)。
- EPS:Electrical Power System(电力系统)。
- Flag Generation:标志生成器(存储敏感信息,也就是参赛者获取的flag)。
卫星设计文档中提到一个重要信息:“The EPS also manages low battery conditions by removing power from non-essential subsystems once a low voltage threshold is reached”表明了如果电压过低,Flag Generation模块可能会被关闭,那么就无法顺利获取flag。因此,我们需要重点关注电源模块EPS,避免因电压过低而无法启动Flag Generation模块。
LaunchDotCom_Carnac_2.pdf中给出与EPS相关的遥测遥控数据中每个参数的含义,如表4-2所示,表中缩放值指的是实际值经过一定比例进行缩放变化后的参数值。
表4-2 与EPS相关的遥测遥控数据中每个参数的含义
参 数 名 称 | 参 数 | 描 述 |
Battery Temp | BATT_TEMP | 缩放值为BATT_TEMP / 10 |
Battery Voltage | BATT_VOLTAGE | 缩放值为BATT_VOLT / 100 +9.0 |
Battery Low Voltage Threshold | LOW_PWR_THRESH | 缩放值为LOW_PWR_THRESH/100+9.0 |
Battery Heater | BATT_HTR | 电池状态为on/off |
Low Power Mode | LOW_PWR_MODE | 低功率模式将关闭非必要的模块 |
Payload Power | PAYLOAD_PWR | Payload Sensors模块电源开关 |
Flag Power | FLAG_PWR | Flag Generation模块的电源开关 |
续表
参 数 名 称 | 参 数 | 描 述 |
ADCS Power | ADCS_PWR | ADCS的电源开关 |
Radio1 Power | RADIO1_PWR | Radio1的电源开关 |
Radio2 Power | RADIO2_PWR | Radio2的电源开关 |
Payload Enable | PAYLOAD_ENABLE | 使能Payload |
Flag Enable | FLAG_ENABLE | 使能Flag Generation |
ADCS Enable | ADCS_ENABLE | 使能ADCS |
Radio1 Enable | RADIO1_ENABLE | 使能Radio1 |
Radio2 Enable | RADIO2_ENABLE | 使能Radio2 |
Command Error Count | CMD_ERR_CNT | 收到的无效命令计数 |
(2)cmd_telemetry_defs.xtce。
cmd_telemetry_defs.xtce描述了卫星的遥测遥控协议。找到EPS相关部分,如下所示:
<xtce:SequenceContainer name="EPS Packet" shortDescription="packet of EPS data">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="BATT_TEMP"/>
<xtce:ParameterRefEntry parameterRef="BATT_VOLTAGE"/>
<xtce:ParameterRefEntry parameterRef="LOW_PWR_THRESH"/>
<xtce:ParameterRefEntry parameterRef="LOW_PWR_MODE"/>
<xtce:ParameterRefEntry parameterRef="BATT_HTR"/>
<xtce:ParameterRefEntry parameterRef="PAYLOAD_PWR"/>
<xtce:ParameterRefEntry parameterRef="FLAG_PWR"/>
<xtce:ParameterRefEntry parameterRef="ADCS_PWR"/>
<xtce:ParameterRefEntry parameterRef="RADIO1_PWR"/>
<xtce:ParameterRefEntry parameterRef="RADIO2_PWR"/>
<xtce:ParameterRefEntry parameterRef="UNUSED1"/>
<xtce:ParameterRefEntry parameterRef="PAYLOAD_ENABLE"/>
<xtce:ParameterRefEntry parameterRef="FLAG_ENABLE"/>
<xtce:ParameterRefEntry parameterRef="ADCS_ENABLE"/>
<xtce:ParameterRefEntry parameterRef="RADIO1_ENABLE"/>
<xtce:ParameterRefEntry parameterRef="RADIO2_ENABLE"/>
<xtce:ParameterRefEntry parameterRef="UNUSED3"/>
<xtce:ParameterRefEntry parameterRef="BAD_CMD_COUNT"/>
</xtce:EntryList>
<xtce:BaseContainer containerRef="AbstractTM Packet Header">
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="CCSDS_VERSION" value="0"/>
<xtce:Comparison parameterRef="CCSDS_TYPE" value="0"/>
<xtce:Comparison parameterRef="CCSDS_SEC_HD" value="0"/>
<xtce:Comparison parameterRef="CCSDS_APID" value="103"/>
</xtce:ComparisonList>
cmd_telemetry_defs.xtce和之前挑战类似。但之前的挑战题只是接收卫星下传的遥测数据,现在多了一个遥控部分,这意味着我们需要将遥控指令发送给卫星。注意到电源模块EPS包定义有许多信息,包括电压参数、低电量阈值等。通过和4.2节类似的解码方式,我们可以获得EPS包的结构如图4-18所示。
图4-18 EPS包结构
参考4.2节的解码方式,可以分为4步:
- 根据XTCE格式,解码出包头APID的值。
- 取出APID为103的数据包,就是电源模块EPS包。
- 针对该数据包,依据图4-18分析出的包结构,提取出感兴趣的各参数值。
- 打印各个参数,进一步得到更多信息,以便于后续分析。
关键代码如下:
import os
import sys
import socket
import random
import time
import bitstring
from pprint import pprint
FLAG_APID = 102
EPS_APID = 103
PAYLOAD_APID = 105
HOST='127.0.0.1'
class cmdSender:
def __init__(self, APID):
self.packet_version = 0
self.packet_type = 1
self.sec_header_flag = 0
self.APID = APID
self.sequence_flags = 3
self.packet_sequence_count = random.randint(1000, 10000)
self.packet_data_length = 1
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, 31335))
line = sock.recv(256)
sock.close()
_, Port = line.split(b" ")[-1].split(b":")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, int(Port)))
time.sleep(1)
while True:
data = sock.recv(6) # 6字节的包头
s = bitstring.BitArray(data)
version, type, sec_header, apid, sequence_flags, sequence_count, data_length = s.unpack('uint:3, uint:1, uint:1, uint:11, uint:2, uint:14, uint:16')
print("APID: {}\nLength: {}\n".format(apid, data_length))
data = sock.recv(data_length+1)
if apid != EPS_APID: # 只解析EPS相关遥测信息
print("Ignoring APID we don't care about")
continue
s = bitstring.BitArray(data)
BATT_TEMP, BATT_VOLTAGE, LOW_PWR_THRESH, LOW_PWR_MODE, BATT_HTR, PAYLOAD_PWR, FLAG_PWR, ADCS_PWR, RADIO1_PWR, RADIO2_PWR, UNUSED1, PAYLOAD_ENABLE, FLAG_ENABLE, ADCS_ENABLE, RADIO1_ENABLE, RADIO2_ENABLE= s.unpack('uint:16, uint:16, uint:16, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1, uint:1')
print("LOW_PWR_MODE:",LOW_PWR_MODE)
#259 Scaled value: BATT_VOLT / 100 + 9.0 11.5
print("BATT_VOLTAGE:",BATT_VOLTAGE)
#400 Scaled value: LOW_PWR_THRESH /100 + 9.0
print("LOW_PWR_THRESH:",LOW_PWR_THRESH)
print("BATT_HTR:",BATT_HTR)
print("PAYLOAD_PWR:",PAYLOAD_PWR)
print("FLAG_PWR:",FLAG_PWR)
print("ADCS_PWR:",ADCS_PWR)
print("RADIO1_PWR",RADIO1_PWR)
print("RADIO2_PWR:",RADIO2_PWR)
print("PAYLOAD_ENABLE:",PAYLOAD_ENABLE)
print("FLAG_ENABLE:",FLAG_ENABLE)
break
运行上述代码后,输出如图4-19所示,成功解析出EPS遥测数据包。
图4-19 解析遥测数据中的EPS包
我们注意到LOW_PWR_MODE=1,如表4-2所示,说明卫星处于低电量模式,可能会关闭一些非核心的系统;FLAG_PWR=0,说明Flag Generation模块的电源是关闭的。因此,首先要做的就是想办法打开Flag Generation模块的供电,进一步从图4-19分析,BATT_VOLTAGE=259,LOW_PWR_THRESH=400,说明低电量模式下电池电压(BATT_VOLTAGE:259)<电池低电压门限(LOW_PWR_THRESH:400)。查看表4-2中BATT_VOLTAGE的描述,259是经过缩放后的值,实际电压根据表中BATT_VOLT / 100 +9.0计算,可得11.59V;同理低电压门限根据表中LOW_PWR_THRESH /100+9.0计算可得,目前的低电压门限设置13V。
根据上述分析,得出解题思路:
- 打开Flag Generation模块电源。
- 关闭低电量模式,可以尝试即修改低电压门限,使其小于实际电压。
步骤一:打开Flag Generation模块电源。
通过查看XTCE文档的遥控指令格式可知,可以通过发送EnableFLAG命令设置FLAG_PWR=1,即打开Flag Generation模块电源。XTCE文档中EnableFLAG命令定义如下:
<xtce:MetaCommand name="AbstractCMD Packet Header" shortDescription="CCSDS CMD Packet Header" abstract="true">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="CCSDS_VERSION"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_TYPE"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_SEC_HD"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_APID"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_GP_FLAGS"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_SSC"/>
<xtce:ParameterRefEntry parameterRef="CCSDS_PLENGTH"/>
</xtce:EntryList>
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="CCSDS_VERSION" value="0"/>
<xtce:Comparison parameterRef="CCSDS_TYPE" value="1"/>
<xtce:Comparison parameterRef="CCSDS_SEC_HD" value="0"/>
<xtce:Comparison parameterRef="CCSDS_GP_FLAGS" value="3"/>
</xtce:ComparisonList>
</xtce:RestrictionCriteria>
</xtce:MetaCommand>
<xtce:MetaCommand name="EnableFLAG" abstract="false">
<xtce:BaseMetaCommand metaCommandRef="AbstractCMD Packet Header">
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="CCSDS_APID" value="103"/>
<xtce:Comparison parameterRef="CCSDS_PLENGTH" value="2"/>
</xtce:ComparisonList>
</xtce:RestrictionCriteria>
</xtce:BaseMetaCommand>
<xtce:ArgumentList>
<xtce:Argument argumentTypeRef="EnableState" name="PowerState"/>
</xtce:ArgumentList>
<xtce:CommandContainer name="ADCSenable">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="CMD"/>
<xtce:ParameterRefEntry parameterRef="PARAM"/>
<xtce:ArgumentRefEntry argumentRef="PowerState"/>
</xtce:EntryList>
<xtce:RestrictionCriteria>
<xtce:Comparison parameterRef="CMD" value="0"/>
<xtce:Comparison parameterRef="PARAM" value="2"/>
</xtce:RestrictionCriteria>
由此可知,需要发送的EnableFLAG命令的结构如下,同时,从从上面定义中注意到,限制标准(RestrictionCriteria)部分,CMD=0,PARAM=2。
6字节包头(header)+“CMD”+“PARAM”+“PowerState”
......
<xtce:ParameterSet>
<xtce:Parameter parameterTypeRef="1ByteInteger" name="CMD" initialValue="4" readOnly="true"/>
<xtce:Parameter parameterTypeRef="1ByteInteger" name="PARAM" initialValue="4" readOnly="true"/>
</xtce:ParameterSet>
<xtce:ArgumentList>
<xtce:Argument argumentTypeRef="EnableState" name="PowerState"/>
</xtce:ArgumentList>
......
<xtce:ArgumentList>
<xtce:Argument argumentTypeRef="EnableState"name="PowerState"/>
</xtce:ArgumentList>
</xtce:IntegerArgumentType>
<xtce:EnumeratedArgumentType name="EnableState" initialValue="ENABLE">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="8" signed="false"/>
<xtce:EnumerationList>
<xtce:Enumeration label="ENABLE" value="1"/>
<xtce:Enumeration label="DISABLE" value="0"/>
</xtce:EnumerationList>
</xtce:EnumeratedArgumentType>
<xtce:StringArgumentType name="Enable">
<xtce:UnitSet/>
......
对于上述参数,寻找对应的参数格式,其中“CMD”和“PARAM”均为1字节。“PowerState”对应的是“EnableState”。“EnableState”为1字节的无符号参数,“PowerState”为“ENABLE”状态对应的值1。
因此需要发送的EnableFLAG命令的结构进一步明确如下:
6字节包头(header)+1字节“CMD”+1字节“PARAM”+1字节“PowerState”
EnableFLAG命令结构如图4-20所示。
图4-20 EnableFLAG命令结构
其中CMD=0,PARAM=2,“PowerState”为“ENABLE”状态对应的值1。最终需要发送的命令如下:
6字节包头结构+ \x00\x02\x01
步骤二:关闭低电量模式。
通过查看XTCE文档可以知道有调整低电量模式阈值的遥控指令LOW_PWR_THRES。当前电压为11.59V,需要找到合理的门限电压值,使得当前的电压11.59V大于门限电压值,从而消除低电量告警。
</xtce:MetaCommand>
<xtce:MetaCommand name="LOW_PWR_THRES" abstract="false">
<xtce:BaseMetaCommand metaCommandRef="AbstractCMD Packet Header">
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="CCSDS_APID" value="103"/>
<xtce:Comparison parameterRef="CCSDS_PLENGTH" value="3"/>
</xtce:ComparisonList>
</xtce:RestrictionCriteria>
</xtce:BaseMetaCommand>
<xtce:ArgumentList>
<xtce:Argument argumentTypeRef="VoltageArgType" name="LW_PWR_THRES"/>
</xtce:ArgumentList>
<xtce:CommandContainer name="ADCSenable">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="CMD"/>
<xtce:ParameterRefEntry parameterRef="PARAM"/>
<xtce:ArgumentRefEntry argumentRef="LW_PWR_THRES"/>
</xtce:EntryList>
<xtce:RestrictionCriteria>
<xtce:Comparison parameterRef="CMD"value="0"/>
<xtce:Comparison parameterRef="PARAM"value="12"/>
</xtce:RestrictionCriteria>
</xtce:CommandContainer>
</xtce:MetaCommand>
其中,“LW_PWR_THRES”对应的参数类型是“VoltageArgType”。因此调整低电量模式的阈值命令的结构为
6字节包头(header)+1字节“CMD”+1字节“PARAM”+2字节“LW_PWR_THRES”(“VoltageArgType”类型)
注意到,限制标准(RestrictionCriteria)部分中,CMD=0,PARAM=12。对于上述参数,在XTCE文档中寻找对应的参数格式,由前文得知,“CMD”和“PARAM”为1字节。提到“LW_PWR_THRES”对应的参数类型是“VoltageArgType”(其定义如下),而“VoltageArgType”为16bit的无符号参数,因此“LW_PWR_THRES”是类型“VoltageArgType”,即16bit的无符号参数。调整低电量模式阈值的包结构如图4-21所示。
<xtce:FloatArgumentType sizeInBits="32" name="VoltageArgType">
<xtce:UnitSet>
<xtce:Unit description="V">:V</xtce:Unit>
</xtce:UnitSet>
<xtce:IntegerDataEncoding sizeInBits="16" encoding="unsigned">
<xtce:DefaultCalibrator>
<xtce:PolynomialCalibrator>
<xtce:Term coefficient="-90.0" exponent="0"/>
<xtce:Term coefficient="100.0" exponent="1"/>
</xtce:PolynomialCalibrator>
图4-21 调整低电量模式阈值的包结构
结合之前观察到的限制标准(RestrictionCriteria)部分中,CMD=0,PARAM=12。进一步明确,需要发送的命令结构如下:
6字节包头(header)+ \x00\x0C+2字节“LW_PWR_THRES”
其中关键是“LW_PWR_THRES”的选取。已知当前电压为11.59V,需要找到合理的门限电压值,使得当前的电压11.59V大于门限电压值。经过尝试,阈值设置高于11.22V即可获取flag。本节设置阈值为11.22V,通过表4-2计算,11.22V对应的是222(十六进制是0xDE)。最终发送的命令为:
6字节包头(header)+ \x00\x0C\xDE
到此,可以给出解答本挑战题的关键代码如下:
import os
import sys
import socket
import random
import time
sys.path.append('./anaconda3/lib/python3.6')
#print(sys.path)
import bitstring
FLAG_APID = 102
EPS_APID = 103
PAYLOAD_APID = 105
HOST='127.0.0.1'
class cmdSender:
def __init__(self, APID):
self.packet_version = 0
self.packet_type = 1
self.sec_header_flag = 0
self.APID = APID
self.sequence_flags = 3
self.packet_sequence_count = random.randint(1000, 10000)
self.packet_data_length = 1
# 定义一个发送遥控指令的函数
def sendCMD(self, socket, payload):
self.packet_data_length = len(payload) - 1
packet = bitstring.pack('uint:3, uint:1, uint:1, uint:11, uint:2, uint:14, uint:16',
self.packet_version, self.packet_type, self.sec_header_flag, self.APID, self.sequence_flags,
self.packet_sequence_count, self.packet_data_length)
packethdr = packet.tobytes()
wholepacket = packethdr + payload # 包头
self.packet_sequence_count += 1
socket.send(wholepacket)
return True
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, 31335))
line = sock.recv(256)
sock.close()
_, Port = line.split(b" ")[-1].split(b":")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, int(Port)))
# 使能Flag Generator模块
cmd = cmdSender(EPS_APID)
time.sleep(1)
cmd.sendCMD(sock, b'\x00\x02\x01') # 发送使能Flag Generator模块命令
# 选取阈值222(11.22V),发送关闭低电量模式命令
cmd.sendCMD(sock, b'\x00\x0c\x03\xde')
# 参考4.2节中的模式解析遥测数据中的flag值
while True:
data = sock.recv(6) #6字节包头
s = bitstring.BitArray(data)
version, type, sec_header, apid, sequence_flags, sequence_count, data_length = s.unpack('uint:3, uint:1, uint:1, uint:11, uint:2, uint:14, uint:16')# 从头解析包数据
print("APID: {}\nLength: {}\n".format(apid, data_length))
data = sock.recv(data_length+1)
if apid != FLAG_APID: # 选出flag包
print("Ignoring APID we don't care about")
continue
s = bitstring.ConstBitStream(data)
char = ' '
flag = ''
while char != '}':
char = chr(s.read('uint:7')) # 取出7bit flag片段,每7bit转换为8bit字符
flag += char
print(flag)
break