施耐德后门

Author Avatar
Xzhah 1月 04, 2019
  • 在其它设备中阅读本文章

IOT

时隔不知道多久,我终于又发博了。自从换了域名忘了做重定向,google就把我的博当抄袭网站处理了,都没时间去搞,再不发博可能更进不了google的索引了,: )。

打算发一个IOT的博文(后面还有两个学着跟的CVE可以发一下)后面再发一个区块链隐蔽通信或者木马的,当然我最爱的二进制!只能寒假学啦呜呜呜。

施耐德后门

赛博地球的杯的题,参考了一下wp。

首先拿到固件NOE77101.bin,首先使用binwalk分析文件

1.png

发现为zlib类型,如上图所示。

使用binwalk –e NOE77101.bin指令提取zlib压缩的文件,解压后的文件385存储在_NOE77101.bin.extracted目录中。

2.png

使用Binwalk对385文件进行分析

3.png

从上图我们可以得到的有效信息为该固件的操作系统版本是VxWorks 2.5,符号表的偏移地址为0x301E74,是大端模式。

使用Binwalk - A命令来获取目标固件的CPU架构等信息,该信息有助于选择正确的反汇编引擎。

4.png

如上图所示目标固件的CPU架构为PowerPC big endian。

将385文件拖入IDA静态分析。

5.png

在Processor type这一项设置为PPC大端模式。

我们在程序某处发现这样一条指令

6.png

Lis在PPC指令集中是相对寻址指令,而源操作数后有@ha这样的特点,查询资料后,我们可以确定基址为0x10000。

7.png

在IDA中设置基址地址为0x10000,可识别函数明显增多。

8.png

那么下一步我们要进行的是函数名修复,因为我们在之前已经得到符号表的地址了。 利用idapython,修复函数名。脚本如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: UTF-8 -*-
from idaapi import *
LoadAddress=0x10000
SymbolStart=0x301e64+LoadAddress
SymbolEnd=0x3293a4+LoadAddress
TempSymbol=SymbolStart
while TempSymbol<SymbolEnd:
MakeStr(Dword(TempSymbol),BADADDR)#创建字符串
sName=GetString(Dword(TempSymbol),-1,ASCSTR_C)#获取字符串
print sName
if sName:
TempFunc=Dword(TempSymbol+4)
MakeName(TempFunc,sName)#重命名
MakeCode(TempFunc)#分析代码区
MakeFunction(TempFunc,BADADDR)
TempSymbol+=16

值得注意的是,我们可以用010Editor打开385,分析其符号表。

9.png

VxWorks系列的字节排序有独特的格式,以16个字节为一组数据,前4个字节是函数名的内存地址,后4个字节是函数的内存位置,然后以另4个特征字节数据+4个字节0x00结尾。所以脚本里我们做的其实就是取前4个字节是函数名字符串,给后4个字节函数的内存位置重命名。 “运行修复脚本后函数名如下。

10.png

那么其实根据函数名我们大概就能推断相关功能了。

我们跟随固件服务加载过程,查看其初始化过程,可以跟踪到一个叫做usrAppInit的函数,发现大量的loginUserAddd调用。同时可以发现多个后门账号,如下图所示。

11.png

可以发现,其账号是明文,而密码是经过loginDefualtEncrypt函数加密后的密文形式。那么,我们既可以点进loginDefualtEncrypt函数逆向分析加密过程,也可以在VxWorksk开发手册上查询vxencrypt加密算法。得到其加密算法如下:

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
int enc(char* in, char* out)
{
int ix;
unsigned long magic = 31695317;
unsigned long passwdInt = 0;
if(strlen(in) <= 0 || strlen(in) > 40)
{
return 1;
}
for(ix = 0; ix < strlen(in);ix++)
passwdInt+= ((in[ix])*(ix+1))^(ix+1);
passwdInt*=magic;
printf("%d",passwdInt);
if(passwdInt == 454293404)
{
printf("key:%s\n",in);
sprintf(out,"%u",passwdInt);
for(ix=0;ix<strlen(out);ix++)
{
if(out[ix]<'3')
out[ix]+='!';
if(out[ix]<'7')
out[ix]+='/';
if(out[ix]<'9')
out[ix]+='B';
}
printf("%s\n",out);
}
return 0;
}

仔细分析此加密算法,我们可以发现其弱点。它是通过一个passwdInt整数来作为中间值。而我们不难发现,passwdInt的0,1,2,3,4,5,6,7,8,9都是一一对应的相应字符Q,R,S,b,c,d,e,y,z,9。那么我们便能轻松地从密文反推出中间值passwdInt。而passwdInt作为一个整数长度有限,我们便能轻松暴力破解得出明文了。注:很好理解的是,会有多个明文得到的密文是相同的。就好比哈希碰撞一样。

代码如下:

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
bool GeneratePassId(char* temp)
{
for (int i=0;i<strlen(temp);i++)
{
if(temp[i]<'3')
temp[i]+='!';
if(temp[i]<'7')
temp[i]+='/';
if(temp[i]<'9')
temp[i]+='B';
}
printf("temp is %s\n",temp);
return true;
}
int enc(char* in, char* out)
{
int ix;
unsigned long magic = 31695317;
unsigned long passwdInt = 0;
if(strlen(in) <= 0 || strlen(in) > 40)
{
return 1;
}
for(ix = 0; ix < strlen(in);ix++)
passwdInt+= ((in[ix])*(ix+1))^(ix+1);
passwdInt*=magic;
//printf("%d",passwdInt);
if(passwdInt == 454293404)
{
printf("key:%s\n",in);
#if 1
sprintf(out,"%u",passwdInt);
//printf("%u",passwdInt);
//printf("%s\n",out);
for(ix=0;ix<strlen(out);ix++)
{
if(out[ix]<'3')
out[ix]+='!';
if(out[ix]<'7')
out[ix]+='/';
if(out[ix]<'9')
out[ix]+='B';
}
printf("%s\n",out);
}
#endif
return 0;
}
char book[]={' ',
'0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'!','"','#','$','%','&','(',')','*','+',',','-','.','/',':',';','<','=','>','?', '@','[','\\',']', '^','-','{','|','}','~',' '};
int main(void)
{
char temp[40]={0};
int PassId;
//sprintf(temp,"%u",454293404);
//GeneratePassId(temp);
char in[40]={0},out[40];
char idx[40];
char k;
//printf("chartable %d",sizeof book);
#if 1
//for(idx[7] = 1; idx[7] < sizeof(book); idx[7]++)
for(idx[6] = 1; idx[6] < sizeof(book); idx[6]++)
{
for(idx[5] = 1; idx[5] < sizeof(book); idx[5]++)
for(idx[4] = 1; idx[4] < sizeof(book); idx[4]++)
for(idx[3] = 1; idx[3] < sizeof(book); idx[3]++)
//for(idx[2] = 1; idx[2] < sizeof(book); idx[2]++)
//for(idx[1] = 1; idx[1] < sizeof(book); idx[1]++)
//for(idx[0] = 1; idx[0] < sizeof(book); idx[0]++)
{
for(k=0;k<7;k++)
in[k]=book[idx[k]];
in[0]='1';in[1]='2';in[2]='3';
enc(in,out);
}
}
#endif
enc("123Txx~",out);
printf("%s\n",out);
enc("=,o0U]IV",out);
printf("%s\n",out);
//scanf("%s",in);
//enc("123456",out);
//printf("%sn",out);
//system("pause");
return 0;
}

得到的结果如下图

12.png

我们选取123Txx~做为明文去验证。为了验证我们的加密程序是否正确,我们同时带入网上别人碰撞出的明文进行检验。

13.png 14.png

所以证明碰撞出的明文是正确的,可以得到目标哈希值