Libinjection 语义分析通用绕过¶
一、漏洞简介¶
二、漏洞影响¶
三、复现过程¶
源码下载地址 https://github.com/client9/libinjection
example.c
#include <stdio.h> #include <strings.h> #include <errno.h> #include "libinjection.h" #include "libinjection_sqli.h" int main(int argc, const char* argv[]) { struct libinjection_sqli_state state; int issqli; const char* input = argv[1]; size_t slen = strlen(input); /* in real-world, you would url-decode the input, etc */ libinjection_sqli_init(&state, input, slen, FLAG_NONE); issqli = libinjection_is_sqli(&state); if (issqli) { fprintf(stderr, "sqli detected with fingerprint of '%s'\n", state.fingerprint); } return issqli; } $ gcc -Wall -Wextra examples.c libinjection_sqli.c $ ./a.out "-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" sqli detected with fingerprint of 's&1UE'
首先给一个pyload
# ./bin "ad1n'-- %a%0aunion select 1,database(),user() -- " not sqli #
测试payload 是否OK
为什么会这样呢???
首先打开源码吧
如果对这个语义分析不太了解的吧,可以去百度上可以找到很多分析的文章这里就不过多阐述了
下图是一个运行的一个流程图
它内部分为四种模式¶
1. 无符号 标准SQL 模式 2. 无符号 MySQL模式 3. 单引号 标准SQL 模式 4. 单引号 MySQL 模式 5. 双引号 MySQL 模式
然后上面是一个单引号的标准SQL 的匹配他的一个核心点在于如下 函数libinjection_sqli_tokenize 作为一个转换内部字符的一个入口
int libinjection_sqli_tokenize(struct libinjection_sqli_state * sf) { pt2Function fnptr; size_t *pos = &sf->pos; stoken_t *current = sf->current; const char *s = sf->s; const size_t slen = sf->slen; if (slen == 0) { return FALSE; } //初始化 st_clear(current); sf->current = current; if (*pos == 0 && (sf->flags & (FLAG_QUOTE_SINGLE | FLAG_QUOTE_DOUBLE))) { *pos = parse_string_core(s, slen, 0, current, flag2delim(sf->flags), 0); printf("单引号双引号进入"); sf->stats_tokens += 1; return TRUE; } while (*pos < slen) { /* * get current character */ const unsigned char ch = (unsigned char) (s[*pos]); /* * look up the parser, and call it * * Porting Note: this is mapping of char to function * charparsers[ch]() */ fnptr = char_parse_map[ch]; *pos = (*fnptr) (sf); if (current->type != CHAR_NULL) { sf->stats_tokens += 1; return TRUE; } } return FALSE; }
为什么# "ad1n'-- %a%0aunion select 1,database(),user() -- "
这么一个简单的可以绕过呢。
首先他是吧 admi'
先进入无符号的标准SQL 然后发现有一个\' 后面就转到
单引号的标准SQL
单引号标准SQL 首先获取的admn'
然后break
继续到了下一层碰到了一个 - 那么走到parse_dash函数中
static size_t parse_dash(struct libinjection_sqli_state * sf) { const char *cs = sf->s; const size_t slen = sf->slen; size_t pos = sf->pos; if (pos + 2 < slen && cs[pos + 1] == '-' && char_is_white(cs[pos+2]) ) { return parse_eol_comment(sf); } else if (pos +2 == slen && cs[pos + 1] == '-') { return parse_eol_comment(sf); } else if (pos + 1 < slen && cs[pos + 1] == '-' && (sf->flags & FLAG_SQL_ANSI)) { sf->stats_comment_ddx += 1; return parse_eol_comment(sf); } else { st_assign_char(sf->current, TYPE_OPERATOR, pos, 1, '-'); return pos + 1; } }
然后当前的pos 一定是符合第一个判断条件的。继续跟踪parse_eol_comment 函数
static size_t parse_eol_comment(struct libinjection_sqli_state * sf) { const char *cs = sf->s; const size_t slen = sf->slen; size_t pos = sf->pos; const char *endpos =(const char *) memchr((const void *) (cs + pos), '\n', slen - pos); if (endpos == NULL) { st_assign(sf->current, TYPE_COMMENT, pos, slen - pos, cs + pos); return slen; } else { st_assign(sf->current, TYPE_COMMENT, pos, (size_t)(endpos - cs) - pos, cs + pos); return (size_t)((endpos - cs) + 1); } }
这里只是判断了是否是有\n 这个。然后就直接进入到st_assign 函数中。继续跟踪st_assign 函数
static void st_assign(stoken_t * st, const char stype,size_t pos, size_t len, const char* value) { const size_t MSIZE = LIBINJECTION_SQLI_TOKEN_SIZE; size_t last = len < MSIZE ? len : (MSIZE - 1); st->type = (char) stype; st->pos = pos; st->len = last; memcpy(st->val, value, last); st->val[last] = CHAR_NULL; }
当前函数也是只是赋值了一下val然后就可以做任何操作。
最后就是 admin'
转换成了S
-- 因为没有查询到直接是C
最后得到的匹配规则为SC 。此匹配规则不在数据库中。完成了绕过。
附一张调试打印图