2014-01-30 40 views
2

看看这个脆弱的片段:什么导致这种格式字符串攻击?

int main(int argc, char **argv) { 
    printf(argv[1], "bla"); 
    return 0; 
} 

没有优化的编译它导致

./test "asd" 
asd 

./test "asd %s" 
asd bla 

./test "asd %0\$s" 
asd %0$s 

./test "asd %45\$s" 
asd XDG_VTNR=7 <-- What the... 

嗯,其实它似乎像 “%(数量)\ $ S” 试图解释(数字)作为一个字符串的第三个参数,向上看堆栈,并且我遇到了我的环境变量。在任何地方使用这种格式字符串,特别是使用奇怪的“\ $”?我找不到任何参考。

最后,编译与优化启用它会导致:

*** invalid %N$ use detected *** 
asd zsh: abort  ./test "asd %46\$s" 

我从来没见过这样的错误。它从何而来?

(我使用的Gentoo Linux/GCC 4.8.2/glibc的2.18)

+0

我还在调试时为什么在我测试的程序的输出中出现字符'X'(而不是'argv [2]'中的最后一个字符)。它与argv [2](紧接着'argv [2]''终止'NUL'字符后面]连续出现。 '(gdb)p sp1 = argv [2] + 4' =>'$ 11 = 0x7fffffffe4a7“XDG_VTNR = 1”'。所以它显然是在只读部分。 –

+0

有趣的是,我发现'argv'的是,如果我检查与'argv [1]'('argv [1] + strlen(argv [1])'')连续的内存,它确实是'argv [2]',但'argv [2]'(最后一个参数)是一个环境变量'“XDG_VTNR = 1”'的连续内存与argv [3]不同,它显然是一个空指针终止列表。 'argv [4]',* whatever *,确实包含了这个内存,并在argv结束后继续下去,显示出大量的环境变量。 –

回答

5

当然,这是mentioned in the manual page像你期望的那样。它似乎来自单一Unix规范(即不是C99)。

它用于国际化,当你需要根据各种信息的顺序调换以适应翻译。数为一个参数的索引:

人们也可以明确地指定通过写"%m$"代替'%'"*m$"代替'*',其中十进制整数m表示哪个参数被采用,在每个在需要的参数的地方,在所期望的参数的自变量列表中的位置,被索引从1

因此,在一个更明智的程序开始,此:

printf("%2$d %1$d", 1, 2); 

个打印

2 1 

这有可能是与优化层楼高启用使编译器执行的代码更重量级的分析,使之能“知道”更多的实际参数列表,并生成一个更好的错误。

+0

好吧....好的。采取的点。尼斯的行为;-)当我启用优化时,我的错误呢? – rralf

+1

@rralf:错误很可能来自您的stdlib printf实现 - 当它看到'%N $'格式以确保它不是不合理时,它会进行一些理智的检查。至于为什么只有在开启优化时才触发,这是一个谜。 –