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, July 15, 2018

NAND中的备用(spare)页

部分节选自:Improving NAND Throughput with Two-Plane and Cache Operations
http://www.macronix.com/Lists/ApplicationNote/Attachments/1907/AN0268V1_Improving%20NAND%20Throughput%20with%20Two-Plane%20and%20Cache%20Operations.pdf

NAND数据的页长可以是
(1)512字节,通常称为“小页”
(2)2 KB,称为 “大页”
(3)或 4 KB页。

最常见的SLC NAND是2KB的页面。

该页面扩展了可寻址空间,通常称为“备用区域”,用于存储ECC、奇偶校验和其他元数据。对于2KB的页面NAND,空闲区域通常是64个字节。因此,每个页面实际上有2112字节的可寻址空间。
目前市场上提供的SLC NAND页面大小如下:
(1)512 字节页+16 字节备用:“小页”。 它只有较小的密度。通常擦除块大小为16 KB,因此这种类型的NAND也称为“小块”NAND。
(2)2 KB页+ 64 字节备用:“大页”。市场上常见的NAND大小从512 MB到8GB。擦除块大小为128 KB。
(3)4 KB页+224 字节备用。常见的尺寸大于4GB。擦除块大小为512 KB。

下面列出了SLC NAND产品中可用的典型命令。
Read Commands
- Page Read
- Cache Read Sequential
- Cache Read Random
- Two-plane Read
- Two-plane Cache Read
Program / Erase Commands
- Page Program
- Cache Program
- Two-plane Program
- Two-plane Cache Program
- Block Erase
- Two-plane Block Erase
ID/ Status Commands
- ID Read
- Status Read / Status Enhance Read
- Reset
Other Commands
- Parameter Page Read (ONFI)
- Feature Set Operation (ONFI)
- Unique ID Read (ONFI)

Tuesday, June 26, 2018

[06] 支持动态语言的程序使用未初始化的内存

https://www.zerodayinitiative.com/advisories/ZDI-18-332/

var int32View = new Int32Array(0×6c);
app.alert(util.printf(”Uninitialized: 0x%04x”, int32View[0]));

Foxit中,当新建一个Int32Array,而且不赋予初始值时,Foxit并没有清零内存,而是直接使用内存。

因此,用户的脚本语言可以得到内存上原始的值。通过特定的创建、释放序列可以泄露函数地址,进而泄露程序基址。

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;
}