有一天,我遇到了一个程序,它允许我从一组预包装的查询中进行选择,然后与服务器通信,以检索所选查询的答案。 当我发现这个程序从来没有要求输入密码与服务器进行验证时,我开始疑惑了。原来,密码是嵌入在程序中的。笔者解释说,这没关系,因为1)它是一个二进制文件,所以没有人能够读到它;2)如果他们能够读到它,他们也找不到密码串。
这两个理由都不成立,我想用这个帖子来证明这一点。我的示例程序只是在终端上写了"密码",但它与实际生产程序的唯一区别是恶意用户需要搜索的字符串数量。
#include <stdio.h>
#include <string.h>
main ()
{
char password [9] = {"secret"};
printf ("This is the password: %sn", password);
}
|
图1 - 内嵌密码的基本程序 |
当执行时,它显示
x1
这是密码:秘密
14:12:16
|
图2 - 基本程序的执行 |
要显示程序模块中的字符串,我们的恶意用户可以使用>system>gnu_library>bin目录下的strings命令。-n5限制了输出5个字符或更长的字符串。我已经截断了输出(. . . . . )以缩短显示,但你可以看到密码是最先显示的字符串之一。将密码与"login"或"password"等关键字或与"admin"、"root"或"sql"等可识别的用户ID相近,可以使搜索更容易。
>system>gnu_library>bin>strings -n5 x1.pm
bind, 17.1.beta.be版本。
bind, Release 17.1.beta.be
Noah_Davids.CAC
释放前
这是密码:%s
秘密
s$start_c_program
抢购清理
_退出
.. . . .
|
图3 - 使用GNU strings命令查找程序模块中所有长于4个字符的字符串。 |
如果strings命令不可用,我们的恶意用户可以直接显示程序模块。我怀疑大家都不小心做了这个动作,虽然不好看,但是会显示密码。我又把输出截断了,同样可以看到密码。
d x1.pm
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x1.pm 11-01-13 14:13:59 mst
`00`01`00`1Abind, Release 17.1.beta.be`00`00`00`00`00`00`00`04ctp
`00`00`00`00`0
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`0
0phx_vos
+#`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`
00`00Noa
.. . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`C
D'`8B`C0
+`00`00`00`00`00这是密码:%s。
`00`00`00`00`00`00`00secret`00`00`00`00`00`00`00`04`B0`00`00`00`00`01`00
`00`00,`
+`00`00`04`00`00`00`03@`00`FF`F8`FF`FE`00`04main`00`00`83`EC`2C`C7D$$`00
.. . .
|
图4--显示找回密码的程序模块 |
在程序中嵌入密码有几种选择。
可以把密码放在一个文件中,程序就可以读取这个文件。只有那些有"需要运行"的人才能获得文件的读取权限,其他人都是空权限。当然"需要运行"未必等于"需要知道密码"。另外总是存在有人误改访问权限的危险。
另一种方法是在程序中保留密码串,只限制对程序的访问。这种解决方案也存在"改变访问列表很容易"的问题。如果程序和其他任何人都可以运行的程序放在同一个目录下,那么更改访问列表的概率就会增加。
最后,你可以将密码保留在程序模块中,但以某种方式对字符串进行混淆。下面的程序展示了一个简单的方法。
#include <stdio.h>
#include <string.h>
main ()
{
char obfuscate [9] = {0x0c, 0x81, 0xe2, 0x94, 0xe6, 0x9c};
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char password [9];
int i;
for (i = 0; i < strlen (obfuscate); i++)
password [i] = obfuscate [i] ^ key [i];
password [i] = 0x0;
printf ("This is the password: %sn", password);
}
|
图5 - 带有混淆密码字符串的程序 |
正如你所看到的,它所做的就是将混淆字符与另一组关键字符进行XOR,从而创建密码。运行该程序会产生与第一个程序相同的输出。
x2
这就是密码:秘密
准备好了 14: 12: 51
|
图6 - 混淆程序的执行情况 |
但搜索字符串时,并没有显示任何类似密码的内容。
>system>gnu_library>bin>strings -n5 x1.pm bind, 17.1.beta.be版本。 bind, Release 17.1.beta.be Noah_Davids.CAC 释放前 这是密码:%s s$start_c_program 抢购清理 _退出 .. . . |
图7 - strings命令不再显示密码。 |
而在显示文件的同时,会显示出混淆的密码字符和关键字符,你要知道到底在哪里找它们,也要知道它们是如何组合成密码的。
d x2.pm
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x2.pm 11-01-13 14:15:27 mst
.. . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`CD'`8B`
C0`00`0
+0`00`00这是密码:%s。
`00`00`00`00`00`00`00`0C`81`E2`94`E6`9C`00`00`00`00`00`00`00`00`00`00`7F
`E4`81`
+E6`83`E8`85`EA`87`00`00`00`00`04`B0`00`00`00`00`01`00`00`00`00`00`00`0
4`01`B0
+`00`03@`00`FF`F0`00`08`FF`FE`00`04main`00`00`83`EC`89$X`89t$T`89|$P`C
7D$L`00
|
图8 - 显示程序模块不再显示可识别的密码。 |
这仍然不是一个完美的解决方案,因为可以通过拆解程序模块来弄清楚密码是如何生成的。不过拆解程序还是需要很高的复杂度,而不是仅仅显示程序模块。
我对将混淆的密码字符串放在一个文件中,然后通过访问列表来保护这个文件有两种想法:一方面,如果需要修改密码,这比将混淆的密码字符串嵌入到程序中要好得多。一方面,如果需要修改密码,这是一个比在程序中嵌入混淆的密码字符串更好的解决方案。另一方面,我怀疑即使文件中还有其他配置选项,将密码字符串放在文件中也会给我们的恶意用户提供更少的潜在密码字符串来调查。 当然,他仍然需要获得对文件的访问权,然后必须找出混淆技术和密钥。
最后一点,我是如何生成混淆密码字符的?XOR操作的好处是((A XOR B) XOR B)等于A,所以我只写了一个简短的程序,把原始密码与我的密钥的字符进行XOR,然后打印结果。
#include <stdio.h>
#include <string.h>
main ()
{
char password [9];
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char newPW [9];
int i;
strcpy (password, "secret");
for (i = 0; i < strlen (password); i++)
{
newPW [i] = password [i] ^ key [i];
printf ("%xn", newPW [i]);
}
}
|
图9 - 生成混淆密码的程序 |