协议分析又来了 WriteUP

Author Avatar
ciaoly 2021年08月13日
  • 在其它设备中阅读本文章

题目来源: 纵横网络靶场社区 (fengtaisec.com);

题目附件

协议分析

打开题目附件, 使用Wireshark进行协议分析. 发现有一系列TCP包频繁出现, 右键单击该TCP包之一, 选择追踪TCP流, 可以看到存在大量的LSIS-XGT字样.

image-20210813081718996

谷歌搜索关键字lsis-xgt protocol manual, 可以找到该协议的手册( XGT FEnet I/F Module - LS Electric ). 另外在CTF-Hub的WriteUp<<协议精准定位分析 | CTFHub>>中也找到一份该协议的手册: LSIS-XGT协议规范书

在手册中查看LSIS-XGT协议的帧结构, 利用Wireshark的Lua插件功能, 编写该协议的解析插件. 导入插件后, 重启Wireshark即可成功解析LSIS-XGT协议. 过滤该协议的包进行分析.

(插件链接: PLC-XGT-protocol-for-Wireshark(github.com)

在Wireshark中添加自定义列, 再Ctrl-A全选所有lsis-xgt协议包, 执行文件 -> 导出分组解析结果 -> As CSV -> Selected packet, 将分组解析结果导出. 使用Excel打开csv文件, 利用筛选功能可以看到所有的指令码.

在Wireshark中添加列 在Excel中看到所有的指令码
image-20210813100213626

在Wireshark中定位到写命令的请求包及其响应包, 发现其负载部分并未有有用信息. 排除所有写命令的包之后, 分析读命令的数据包.

依然使用Excel进行分析, 可以发现存在大量的lsis_xgt.data_count == 100的数据包.

变量长度 数据长度
image-20210813103801298 image-20210813103810322

在wireshark中依次浏览lsis_xgt.data_count != 100的数据包, 发现均无有用信息. 此时基本上排除上述无意义的包, 过滤lsis_xgt.data_count == 100, 尝试提取有用信息.

*到这里我就卡住了, 通过查阅WriteUp, 下一步是将所有的 lsis_xgt.data_address matches "%PB...\*00$的读命令的响应包过滤, 将其中的负载提取出来进行进一步处理. 通过过滤也可以看到, 这部分地址是很规整的依次递增排布的, 很有可能是一次完整的文件传输操作*

提取数据

尝试利用脚本将所有的 lsis_xgt.data_address matches "%PB...\*00$的读命令的响应包的负载提取出来. 首先添加过滤器lsis_xgt.data_count == 100, 在Wireshark中执行文件 -> 导出分组解析结果 -> As Json -> All packets/Displayed, 将所有的包导出为Json文件.

编写PowerShell脚本, 将有效的负载提取出来.

$json = (Get-Content .\temp.json | ConvertFrom-Json );
$lsis = $json | ForEach-Object { $_._source.layers.lsis_xgt };
# $validDat = ($lsis | Where-Object {($_.'lsis_xgt.data_count' -eq "100") -and ($_.'lsis_xgt.frame_direction' -eq "0x00000011" ) });
# $validDat = $validDat | ForEach-Object { $_.'lsis_xgt.data' -replace ":", "" };

$packLen100 = $lsis | Where-Object { $_.'lsis_xgt.data_count' -eq "100" };
$validDat = @();
$len = $packLen100.Count;
for ($i = 0; $i -lt $len; $i += 2) {
    # 相当于过滤 lsis_xgt.data_address matches "%PB...\*00$
    if ($packLen100[$i].'lsis_xgt.data_address' -like "*30:30" ) {
        # $i是请求包, $i+1是响应包
        $validDat += $packLen100[$i + 1].'lsis_xgt.data' -replace ":", "" ;
    }
}

# 将hex字符串转换为byte, 并与0xff进行异或运算后导出为二进制文件.
$bytes = @();
$validDat | ForEach-Object {
    $bytes += (( [byte[]] -split ( $_ -replace "..", '0x$& ')) | ForEach-Object {
            $_ -bxor 0xff;
        });
};
[System.IO.File]::WriteAllBytes("output.bin", $bytes);

利用HexEditor打开导出的output.bin, 发现文件魔数为"MZ". 重命名为output.exe使用ExeInfoPe读取, 是64位的C# GUI程序.

image-20210813140932916

调试程序

将程序导入到DnSpy x64中反编译(很好, 没有混淆, 庆幸), 首先进行静态调试.

程序的入口和主窗体初始化函数如下所示:

程序入口 主窗体初始化
image-20210813142248393 image-20210813142122769

可以看到, 在主窗体初始化时, 修改了程序的可见性, 这会使得程序运行后在屏幕上"消失不见". 在这里设置断点, 尝试利用动态调试的方式修改该值使其显示.

查看主窗体的布局文件mainwindow.baml, 其内容如下所示:

<Window
    Title="MainWindow"
    Height="450"
    Width="800">
    <Grid>
        <!--此处省略若干RadioButton-->
        <RadioButton
            Content=""
            HorizontalAlignment="Left"
            Margin="733,138,0,0"
            VerticalAlignment="Top" />
        <Label
            Name="lable1csc"
            Content="1csc"
            HorizontalAlignment="Left"
            Height="154"
            Margin="26,36,0,0"
            VerticalAlignment="Top"
            Width="745"
            Background="#FFFFFFFF" />
    </Grid>
</Window>

窗体中含有大量的RadioButton和一个Label. 在DnSpy中运行程序, 在断点处修改base.Visibility的值为Visible(要先执行完语句base.Visibility = Hidden之后再修改内存).

继续运行, 程序窗体正常显示. 但是窗体中只有一个Label, 显示1csc, 没有其它元素.

image-20210813143608504

在xaml中看到有很多RadioButton, 在运行后的主窗体中却看不到这些单选按钮. 因此这些单选按钮可能是得到flag的关键.

MainWindow的初始化方法中并没有发现隐藏RadioButton的代码, 重新审查mainwindow.baml也没有发现为RadioButton设置隐藏属性. 进一步的审查mainwindow.baml时发现, 主窗体的宽高为800x450, 而Label控件的宽高为745x154, 因此RadioButton控件不显示的原因很可能是被Label控件遮挡了.

重新审查MainWindow的初始化方法, 发现主窗体对象MainWindow有一个私有成员label1csc, 它会在初始化xaml布局后被赋值为Label对象的引用. 利用该引用, 可以修改LabelVisibility属性, 将其隐藏, 从而使被遮挡的RadioButton显示出来.

在DnSpy中重新运行该程序, 重复上述操作, 在断点处单步调试, 先修改主窗体的可见性为Visible, 再找到主窗体的成员label1csc, 将其可见性修改为Hidden. 如下所示:

在局部变量中找到label1csc 修改Label的可见性为Hidden
image-20210813150800885

继续运行程序, 可以看到此时RadioButton均已显示. 得到一串字符: ICSC-F1A!.

image-20210813151015622

得到flag{ICSC-F1A!}

PS: 其实除了动态调试的方法之外, 还可以将mainwindow.baml中的内容导出, 用正则处理一下, 翻译成HTML, 使用绝对定位的方式模拟该布局. 也可以得到flag.

image-20210813152631271

在线查看该HTML


参考文章: