0x00 前言


    当前比较流行也比较有效率的挖掘的漏洞的方法是Fuzzing,当然这也需要花点时间去写Fuzzing程序。然而不是每个东西都有必要去写Fuzzing程序,也不是每个东西都可以去Fuzzing的,所以也还是要继续修炼我们的二进制安全流派内功—“肉眼挖洞神功”。

    安全漏洞在源代码层面和汇编代码层面会有不同的表现,想要进阶升级的话,必须清晰的了解每种安全漏洞类型在源代码层面和汇编代码层面都是长什么样的。有些安全漏洞在源代码层面容易发现它,有些安全漏洞则在汇编代码层面比较容易发现它。不管是源代码层面还是汇编代码层面,普通人看代码的时候即使看到 有安全漏洞的那些代码的时候,往往是“只见代码,不见漏洞”。修炼“肉眼挖洞神功”的目标是看到那些疑似有问题的代码时候能够警觉,能够识别,并快速判定 是否可形成安全漏洞。

0x01 源代码层面


1. Link Attack


*unix 下的 link attack,有意识到吗?

if(access("file",W_OK)!=0){
    exit(1);
}
fd = open("file",O_WRONLY);

write(fd,buffer,sizeof(buffer));

2. Integer Overflow Example in OpenSSH 3.1


发生在真实的 openssh 3.1 ,有漏洞吗?

u_int nresp;
...
nresp = packet_get_int();
if(nresp){
    response = xmalloc(nresp * sizeof(char*));
    for(i=0; i<nresp; i++)
        response[i] = packet_get_string(NULL);
}
packet_check_eom();

3. Signed Integer Vulnerability Example


注意×××符号,能被绕过吗?

int read_user_data(int sockfd) 
{
  int length, sockfd, n;
  char buffer[1024];
  length = get_user_length(sockfd);
  if(length > 1024){
     error("illegal input, not enough room in buffer\n"); 
     return 1;
  }
  if(read(sockfd, buffer, length) < 0){ error("read: %m");
     return 1;
  }
  return 0; 
}

4. Truncation Vulnerability Example in NFS


×××截断问题?

void assume_privs(unsigned short uid) {
  seteuid(uid);
  setuid(uid);
}
int become_user(int uid) 
{
  if (uid == 0)
     die("root isnt allowed");
  assume_privs(uid); 
}

5. 苹果SSL/TLS 重大安全漏洞的细节


(CVE-2014-1266)多个goto fail造成重大安全隐患。

static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
                 uint8_t *signature, UInt16 signatureLen)
{
  ...
  if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
    goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)
    goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;  <---- *** DANGER ***
  if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;
    err = sslRawVerify(ctx,
             ctx->peerPubKey,
             dataToSign,			            /* plaintext */
             dataToSignLen,				   /* plaintext length */
             signature,
             signatureLen);
  ...
fail:
  SSLFreeBuffer(&signedHashes);
  SSLFreeBuffer(&hashCtx);
  return err;
}

6. MS-RPC DCOM Buffer Overflow?(冲击波)


冲击波蠕虫只因为一个缓冲区溢出。

HRESULT GetMachineName (WCHAR * pwszPath) {
  The WCHAR wszMachineName [N + 1]); 
  ...
  LPWSTR pwszServerName = wszMachineName;
  while (* pwszPath! = L '\\')
    * PwszServerName + + = * pwszPath + +;   
  ...
}

7. 有漏洞吗?


unsigned short read_length(int sockfd) 
{
  unsigned short len;
  if(full_read(sockfd, (void *)&len, 2) != 2)
    die("could not read length!\n");
  return ntohs(len);
}
int read_packet(int sockfd) 
{
   struct header hdr;
   short length;
   char *buffer;
   length = read_length(sockfd);
   if(length > 1024){
     error("read_packet: length too large: %d\n", length); 
     return 1;
   }
   buffer = (char *)malloc(length+1);
   if((n = read(sockfd, buffer, length) < 0){
     error("read: %m");
     free(buffer);
     return 1;
   }
   buffer[n] = '\0';
   return 0; 
}

8. 有漏洞吗?


提示: 5rOo5oSPc216ZW9m

char *read_username(int sockfd) 
{
  char *buffer, *style, userstring[1024]; 
  int i;
  buffer = (char *)malloc(1024);
  if(!buffer){
    error("buffer allocation failed: %m"); 
    return NULL;
  }
  if(read(sockfd, userstring, sizeof(userstring)-1) <= 0){ 
     free(buffer);
     error("read failure: %m");
     return NULL;
  }
  userstring[sizeof(userstring)-1] = '\0';
  style = strchr(userstring, ':'); 
  if(style)
     *style++ = '\0';
  sprintf(buffer, "username=%.32s", userstring);
  if(style)
    snprintf(buffer, sizeof(buffer)-strlen(buffer)-1,
         ", style=%s\n", style);
  return buffer;
}

9. 有漏洞吗?


提示:

enter p_w_picpath description here

/* special thing for ldap.
* The parts are separated by question marks. 
* From RFC 2255:
* ldapurl = scheme "://" [hostport] ["/"
*		   [dn ["?" [attributes] ["?" [scope] 
*		   ["?" [filter] ["?" extensions]]]]]]
*/
if (!strncasecmp(uri, "ldap", 4)) 
{
  char *token[5];
  int c = 0;
  token[0] = cp = ap_pstrdup(p, cp); 
  while (*cp && c < 5) {
    if (*cp == '?') {
      token[++c] = cp + 1;
      *cp = '\0';
    }
    ++cp; 
  }

10. 有漏洞吗?(Antisniff v1.1 Vulnerability)


tips

char *indx;
int count;
char nameStr[MAX_LEN]; //256
...
memset(nameStr, '\0', sizeof(nameStr));
...
indx = (char *)(pkt + rr_offset); 
count = (char)*indx;
while (count){
   if (strlen(nameStr) + count < ( MAX_LEN - 1) ){
     (char *)indx++;
     strncat(nameStr, (char *)indx, count); 
     indx += count;
     count = (char)*indx;
     strncat(nameStr, ".",
          sizeof(nameStr) strlen(nameStr));
   } else {
     fprintf(stderr, "Alert! Someone is attempting "
                "to send LONG DNS packets\n");
     count = 0; }
}
nameStr[strlen(nameStr)-1] = '\0';

11. 还有漏洞吗?(Antisniff v1.1.1 Vulnerability)


char *indx;
int count;
char nameStr[MAX_LEN]; //256
...
memset(nameStr, '\0', sizeof(nameStr));
...
indx = (char *)(pkt + rr_offset); 
count = (char)*indx;

while (count){
/* typecast the strlen so we aren't dependent on the call to be properly setting to unsigned. */

  if ((unsigned int)strlen(nameStr) + 
    (unsigned int)count < ( MAX_LEN - 1) ){
    (char *)indx++;
    strncat(nameStr, (char *)indx, count); 
    indx += count;
    count = (char)*indx;
    strncat(nameStr, ".",
            sizeof(nameStr) strlen(nameStr)); 
  } else {
    fprintf(stderr, "Alert! Someone is attempting " 
                    "to send LONG DNS packets\n");
    count = 0; 
  }
}
nameStr[strlen(nameStr)-1] = '\0';

12. 还有漏洞吗?(Antisniff v1.1.2 Vulnerability)


unsigned char *indx;
unsigned int count;
unsigned char nameStr[MAX_LEN]; //256
...
memset(nameStr, '\0', sizeof(nameStr));
...
indx = (char *)(pkt + rr_offset); 
count = (char)*indx;
while (count){
   if (strlen(nameStr) + count < ( MAX_LEN - 1) ){ 
     indx++;
     strncat(nameStr, indx, count);
     indx += count;
     count = *indx;
     strncat(nameStr, ".",
          sizeof(nameStr) strlen(nameStr));
    } else {
     fprintf(stderr, "Alert! Someone is attempting "
                "to send LONG DNS packets\n");
     count = 0; 
    }
}
nameStr[strlen(nameStr)-1] = '\0';

0x02 汇编代码层面


接下要在汇编代码堆里磨练下,下面的汇编代码有安全问题吗?都是些什么问题?

1. Safe or vulnerability?


text:0040106B sub_40106B      proc near               ; CODE XREF: _main+58p
.text:0040106B
.text:0040106B var_10004       = byte ptr -10004h
.text:0040106B var_4           = dword ptr -4
.text:0040106B arg_0           = word ptr  8
.text:0040106B
.text:0040106B                 push    ebp
.text:0040106C                 mov     ebp, esp
.text:0040106E                 mov     eax, 10004h
.text:00401073                 call    __alloca_probe
.text:00401078                 mov     eax, dword_404020
.text:0040107D                 xor     eax, ebp
.text:0040107F                 mov     [ebp+var_4], eax
.text:00401082                 movsx   eax, [ebp+arg_0]
.text:00401086                 movsx   eax, [ebp+eax+var_10004]
.text:0040108E                 push    eax
.text:0040108F                 push    offset Format   ; "t %x"
.text:00401094                 call    ds:printf
.text:0040109A                 pop     ecx
.text:0040109B                 pop     ecx
.text:0040109C                 mov     ecx, [ebp+var_4]
.text:0040109F                 xor     ecx, ebp
.text:004010A1                 xor     eax, eax
.text:004010A3                 call    sub_401BD2
.text:004010A8                 leave

2. Safe or vulnerability?


text:004010AA sub_4010AA      proc near               ; CODE XREF: _main+60p
.text:004010AA
.text:004010AA var_190         = dword ptr -190h
.text:004010AA arg_0           = dword ptr  8
.text:004010AA
.text:004010AA                 push    ebp
.text:004010AB                 mov     ebp, esp
.text:004010AD                 mov     eax, [ebp+arg_0]
.text:004010B0                 sub     esp, 190h .text:004010B6                 cmp     eax, 64h .text:004010B9                 jle     short loc_4010BE .text:004010BB                 push    64h .text:004010BD                 pop     eax .text:004010BE .text:004010BE loc_4010BE:                             ; CODE XREF: sub_4010AA+Fj
.text:004010BE                 push    [ebp+eax*4+var_190]
.text:004010C5                 push    offset Format   ; "t %x"
.text:004010CA                 call    ds:printf
.text:004010D0                 pop     ecx

3. Safe or vulnerability?


.text:00401000 sub_401000      proc near               ; CODE XREF: _main+48p
.text:00401000
.text:00401000 var_190         = dword ptr -190h
.text:00401000 arg_0           = dword ptr  8
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 mov     eax, [ebp+arg_0]
.text:00401006                 sub     esp, 190h .text:0040100C                 cmp     eax, 64h .text:0040100F                 jbe     short loc_401014 .text:00401011                 push    64h .text:00401013                 pop     eax .text:00401014 .text:00401014 loc_401014:                             ; CODE XREF: sub_401000+Fj
.text:00401014                 push    [ebp+eax*4+var_190]
.text:0040101B                 push    offset Format   ; "t %x"
.text:00401020                 call    ds:printf
.text:00401026                 pop     ecx
.text:00401027                 pop     ecx

4. Safe or vulnerability?


.text:004010D6 sub_4010D6      proc near               ; CODE XREF: _main+68p
.text:004010D6
.text:004010D6 arg_0           = dword ptr  4
.text:004010D6
.text:004010D6                 push    esi
.text:004010D7                 push    64h             ; Size
.text:004010D9                 call    ds:malloc
.text:004010DF                 mov     esi, eax
.text:004010E1                 mov     eax, [esp+8+arg_0]
.text:004010E5                 cmp     eax, 64h
.text:004010E8                 pop     ecx
.text:004010E9                 jle     short loc_4010EE
.text:004010EB                 push    64h
.text:004010ED                 pop     eax
.text:004010EE
.text:004010EE loc_4010EE:                             ; CODE XREF: sub_4010D6+13j
.text:004010EE                 movsx   eax, byte ptr [eax+esi]
.text:004010F2                 push    eax
.text:004010F3                 push    offset Format   ; "t %x"
.text:004010F8                 call    ds:printf
.text:004010FE                 push    esi             ; Memory
.text:004010FF                 call    ds:free

5. Safe or vulnerability?


. text:0040110C sub_40110C      proc near               ; CODE XREF: _main+70p
.text:0040110C
.text:0040110C var_19A0        = byte ptr -19A0h
.text:0040110C var_4           = dword ptr -4
.text:0040110C arg_0           = word ptr  8
.text:0040110C
.text:0040110C                 push    ebp
.text:0040110D                 mov     ebp, esp
.text:0040110F                 mov     eax, 19A0h
.text:00401114                 call    __alloca_probe
.text:00401119                 mov     eax, dword_404020
.text:0040111E                 xor     eax, ebp
.text:00401120                 mov     [ebp+var_4], eax
.text:00401123                 movsx   eax, [ebp+arg_0]
.text:00401127                 movsx   eax, [ebp+eax+var_19A0]
.text:0040112F                 push    eax
.text:00401130                 push    offset Format   ; "t %x"
.text:00401135                 call    ds:printf
.text:0040113B                 pop     ecx
.text:0040113C                 pop     ecx
.text:0040113D                 mov     ecx, [ebp+var_4]
.text:00401140                 xor     ecx, ebp

6. Safe or vulnerability?


.text:0040102C sub_40102C      proc near               ; CODE XREF: _main+50p
.text:0040102C
.text:0040102C var_10004       = byte ptr -10004h
.text:0040102C var_4           = dword ptr -4
.text:0040102C arg_0           = word ptr  8
.text:0040102C
.text:0040102C                 push    ebp
.text:0040102D                 mov     ebp, esp
.text:0040102F                 mov     eax, 10004h
.text:00401034                 call    __alloca_probe
.text:00401039                 mov     eax, dword_404020
.text:0040103E                 xor     eax, ebp
.text:00401040                 mov     [ebp+var_4], eax
.text:00401043                 movzx   eax, [ebp+arg_0]
.text:00401047                 movsx   eax, [ebp+eax+var_10004]
.text:0040104F                 push    eax
.text:00401050                 push    offset Format   ; "t %x"
.text:00401055                 call    ds:printf
.text:0040105B                 pop     ecx

7. Safe or vulnerability?


. .text:0040118A ; int __cdecl sub_40118A(LPCSTR lpMultiByteStr)
.text:0040118A sub_40118A      proc near               ; CODE XREF: _main+91p
.text:0040118A
.text:0040118A WideCharStr     = word ptr -44h
.text:0040118A var_4           = dword ptr -4
.text:0040118A lpMultiByteStr  = dword ptr  8
.text:0040118A
.text:0040118A                 push    ebp
.text:0040118B                 mov     ebp, esp
.text:0040118D                 sub     esp, 44h .text:00401190                 mov     eax, dword_404020 .text:00401195                 xor     eax, ebp .text:00401197                 mov     [ebp+var_4], eax .text:0040119A                 mov     ecx, [ebp+lpMultiByteStr] .text:0040119D                 mov     eax, ecx .text:0040119F                 push    esi .text:004011A0                 lea     esi, [eax+1] .text:004011A3 .text:004011A3 loc_4011A3:                             ; CODE XREF: sub_40118A+1Ej
.text:004011A3                 mov     dl, [eax]
.text:004011A5                 inc     eax
.text:004011A6                 test    dl, dl
.text:004011A8                 jnz     short loc_4011A3
.text:004011AA                 push    40h             ; cchWideChar
.text:004011AC                 lea     edx, [ebp+WideCharStr]
.text:004011AF                 push    edx             ; lpWideCharStr
.text:004011B0                 sub     eax, esi .text:004011B2                 push    eax             ; cbMultiByte
.text:004011B3                 push    ecx             ; lpMultiByteStr
.text:004011B4                 push    0               ; dwFlags
.text:004011B6                 push    0               ; CodePage
.text:004011B8                 call    ds:MultiByteToWideChar

8. Safe or vulnerability?


text:00401738 Size            = dword ptr  4
.text:00401738 Src             = dword ptr  8
.text:00401738
.text:00401738                 push    esi
.text:00401739                 push    edi
.text:0040173A                 mov     edi, [esp+8+Size]
.text:0040173E                 lea     eax, [edi+1]
.text:00401741                 push    eax             ; Size
.text:00401742                 call    ds:__imp__malloc
.text:00401748                 mov     esi, eax
.text:0040174A                 test    esi, esi
.text:0040174C                 pop     ecx
.text:0040174D                 jz      short loc_401774
.text:0040174F                 push    edi             ; Size
.text:00401750                 push    [esp+0Ch+Src]   ; Src
.text:00401754                 push    esi             ; Dst
.text:00401755                 call    _memcpy
.text:0040175A                 push    esi
.text:0040175B                 push    offset aS       ; "%s\n"
.text:00401760                 mov     byte ptr [esi+edi], 0
.text:00401764                 call    ds:__imp__printf

0x04 最后


汇编代码层面的例子需要想想它们的源代码都是怎么写的才能编译出来这些有安全漏洞的代码。做到源代码层面和汇编代码层面通吃。更要做到从宏观上了解程序世界功能和规则,从微观上了解其实现细节。

上面的例子都不到一百行的汇编代码,都找出问题了吗?决战汇编代码就是需要我们在真实程序世界的上千万行汇编代码中找出那些隐藏的安全漏洞,这个难度会高很多。其中有对抗有编程者的防御措施,需要思考如何打败它,是和程序世界的缔造者们的一次跨时间和空间无声的较量。

以上这些其实是一个引子,需要小伙伴继续深入研究推演和锻炼。推荐学习Mark Down & JohnMcDonald两位大牛的书《The Art of Software Security Assessment-- Identifying andavoiding software vulnerabilities》(某些例子来自该书),希望对小伙伴们有所帮助。