主页 > 开源代码  > 

CTF-PWN-小tips

CTF-PWN-小tips

文章目录 overflowscanfgetreadstrcpystrcat Find string in gdbgdbgdb peda Binary ServiceFind specific function offset in libc手工自动 Find '/bin/sh' or 'sh' in library手动自动 Leak stack addressFork problem in gdbSecret of a mysterious section - .tlsPredictable RNG(Random Number Generator)Use one-gadget-RCE instead of systemHijack hook functionUse printf to trigger malloc and freeUse execveat to open a shell

overflow

假设:定义了char buf[40] signed int num

scanf scanf(“%s”, buf) %s没有边界检查,可以溢出scanf(“%39s”, buf) %39仅从输入中获取39个字节,并将NULL字节放在输入末尾,无法溢出scanf(“%40s”, buf) 从输入中获取40个字节,但还会在输入末尾的位置放NULL字节,存在单字节NULL溢出scanf(“%d”, &num) 结合alloca(num)使用,如果设置num为负值,会出现从栈上分配的堆与原来的栈帧重合 大多数时候,程序只检查了上界而没有忘记num可能为负数(或者说忘了设置num为无符号数),此时将num输入为负数可能有意外的效 果

alloca()是在栈(stack)上申请空间的

get gets(buf) 没有边界检查,可溢出fgets(buf,40,stdin) 从输入中获取39个字节,把NULL字节放在输入末尾,无用 read read(stdin, buf, 40) fread(buf, 1, 40, stdin) 从输入中获取40个字节,并且不会在末尾放置NULL字节,可能存在信息泄露 例如 0x7fffffffdd00: 0x4141414141414141 0x4141414141414141 0x7fffffffdd10: 0x4141414141414141 0x4141414141414141 0x7fffffffdd20: 0x4141414141414141 0x00007fffffffe1cd 此时如果用printf或puts用于输出buf,它将一直输出,直到NULL字节 此时可以得到’A’*40 + ‘\xcd\xe1\xff\xff\xff\x7f’,从而信息泄露成功 strcpy

假设:定义char buf2[60]

strcpy(buf,buf2) strcpy()函数将源字符串buf2 的每个字节拷贝到目的字符串buf 中,直到到达NULL字节,buf2字符串末尾的NULL字节也被拷贝过去,buf2可能比buf长,所以可能溢出stncpy(buf,buf2,40) memcpy(buf,buf2,40) 将40个字节从buf2复制到buf,但不会在末尾放置NULL字节,由于没有NULL字节,可能存在信息泄露,如printf或puts该内容时,同上面的read的信息泄露 strcat strcat(buf,buf2) 把 buf2 所指向的字符串追加到 buf 所指向的字符串的结尾。如果buf不够大,可能会导致溢出 会把NULL字节放在末尾,可能会导致一字节溢出 在某些情况下,可以使用该NULL字节正好覆盖储存堆栈地址的最低位字节strncat(buf, buf2, n) strncat() 在strcat() 的基础上增加第三个参数,其中第三个参数限制添加的最大字符数,其他跟strcat() 一样:把拼接后的字符串作为新的第一个字符串同时也会把NULL字节放在末尾,第二个字符串的值不改变;返回值是第一个参数的地址 也可能会有单字节NULL溢出 Find string in gdb

environ利用 通过libc找到environ地址后,泄露environ地址处的值,可以得到环境变量地址,环境变量保存在栈中,通过偏移可以得到栈上任意变量的地址。 ssp((Stack Smashing Protect) )攻击 检测到stack smash时,__stack_chk_fail函数会在报错信息中会打印出libc_argv[0]的值,而libc_argv[0]指向的则是程序名。 若我们能够栈溢出足够的长度,覆盖到__libc_argv[0]的位置,那我们就能让程序打印出任意地址的数据,造成任意地址数据泄露。这就是ssp攻击。

在SSP攻击中,我们需要找到argv[0]和输出开始地址之间的距离

gdb

gdb print打印

在gdb中使用p/x ((char **)environ),argv[0]的地址将是打印的地址值 - 0x10 (gdb) p/x (char **)environ # 以十六级进制形式打印 $9 = 0x7fffffffde38 (gdb) x/gx 0x7fffffffde38-0x10 #以一个八个字节为单元十六进制形式显示一个单元地址的内容 0x7fffffffde28: 0x00007fffffffe1cd (gdb) x/s 0x00007fffffffe1cd#显示地址对应的字符串 0x7fffffffe1cd: "/home/naetw/CTF/seccon2016/check/checker" gdb peda

使用 searchmem "/home“可找到argv[0]的位置,如图在

[stack] : 0x7fffffffe2cc (“/home/llk/桌面/exp/dui/b00ks”)

Binary Service

ncat 将二进制文件转换为IP地址和端口

加载顺序为LD_PRELOAD > LD_LIBRARY_PATH > /etc/ld.so.cache > /lib>/usr/lib LD_PRELOAD(not LD_PRELOAD_PATH) 是要在任何其他库之前加载的特定库 ( files ) 的列表,无论程序是否需要。LD_LIBRARY_PATH是在加载无论如何都会加载的库时要搜索的 目录列表。

ncat -vc ./二进制文件名 -kl 127.0.0.1 端口号

当需要修改动态链接库的时候

ncat -vc ‘LD_PRELOAD=/path/to/libc.so ./binary’ ./二进制文件名 -kl 127.0.0.1 端口号ncat -vc ‘LD_LIBRARY_PATH=/path/of/libc.so ./binary’ ./二进制文件名 -kl 127.0.0.1 端口号

然后可以通过nc 127.0.0.1 端口号来允许该程序

Find specific function offset in libc

当我们成功泄露函数地址时,可以得到libc的基地址通过得到函数地址减去函数的偏移

readelf是用来读取elf文件相关信息的

手工 readelf -s libc文件 | grep 函数名 自动 使用pwntools提供的函数 from pwn import * libc = ELF('libc文件') system_off = libc.symbols['函数'] Find ‘/bin/sh’ or ‘sh’ in library

objdump反汇编文件

手动 strings -tx libc文件 | grep 字符串 自动 from pwn import * libc = ELF('libc.so') ... sh = base + next(libc.search('sh\x00')) binsh = base + next(libc.search('/bin/sh\x00')) Leak stack address 已经泄露libc基地址可以泄露输入的地址内容

在libc中的environ变量存储的地址和main函数的第三个参数一样,即char **envp,我们可以通过libc中的environ变量泄露栈地址

Fork problem in gdb 可以使用set follow-fork-mode mode来设置fork跟随模式。进入gdb以后,我们可以使用show follow-fork-mode来查看目前的跟踪模式。 show follow-fork-modeset follow-fork-mode parent gdb只跟踪父进程,不跟踪子进程,这是默认的模式。set follow-fork-mode child gdb在子进程产生以后只跟踪子进程,放弃对父进程的跟踪。

想同时调试父进程和子进程,以上的方法就不能满足了。Linux提供了set detach-on-fork mode命令来供我们使用

show detach-on-fork show detach-on-fork显示了目前的detach-on-fork模式

set detach-on-fork on 只调试父进程或子进程的其中一个(根据follow-fork-mode来决定),这是默认的模式。 另一个进程会独立运行

set detach-on-fork off 父子进程都在gdb的控制之下,其中一个进程正常调试(根据follow-fork-mode来决定),另一个进程会被设置为暂停状态。另一个进程 block 在 fork 位置

在使用“set detach-on-fork off”命令后,用“i inferiors”(i是info命令缩写)查看进程状态,可以看到父子进程都在被gdb调试的状态,前面显示“*”是正在调试的进程。我们可以使用inferior x(x是i inferiors的显示出的进程的num)来切换到对应的进程去调试

Secret of a mysterious section - .tls 需要malloc时候并且可以分配任意大小能够读出任意地址的内容

一般来说当malloc分配大小大于0x21000时候会使用mmap分配,此时分配的内存区域正好在.tls段之前

.tls段存储了一些有用的信息,例如main_arena的地址,canary的地址,还有一个栈上的地址,该地址总是和指向栈上的具有固定偏移量的位置

Predictable RNG(Random Number Generator)

当程序使用RNG生成得随机数的值来作为某些重要信息的地址时,我们可以使用ctypes来调用动态链接库或共享库中的函数 ctypes是python的一个函数库,提供和C语言兼容的数据类型,可以直接调用动态链接库中的导出函数。

例如如下代码

srand(time(NULL)); while(addr <= 0x10000){ addr = rand() & 0xfffff000; } secret = mmap(addr,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS ,-1,0); if(secret == -1){ puts("mmap error"); exit(0); }

我们可以得到相同的addr通过以下的手段

import ctypes LIBC = ctypes.cdll.LoadLibrary('libc.so.6') LIBC.srand(LIBC.time(0)) addr = LIBC.rand() & 0xfffff000

ctypes.cdll.LoadLibrary(动态链接库)加载动态库,然后就可以调用动态库中的函数。

Use one-gadget-RCE instead of system 泄露了libc的基地址任意地址可写

使用onegadget工具寻找能够getshell的gadget,但一般会有寄存器的要求限制,满足就好

Hijack hook function 泄露了libc基址可以任意地址写程序使用到malloc free realloc void (*hook) (void *, const void *) = atomic_forced_read (__free_hook); if (__builtin_expect (hook != NULL, 0)) { (*hook)(mem, RETURN_ADDRESS (0)); return; }

检查_free_hook的值是否为空,不为空则调用跳转到位置执行该位置对应的函数。

Use printf to trigger malloc and free

printf有时会引发malloc,当然引发了malloc最后也会引发free

#define EXTSIZ 32 enum { WORK_BUFFER_SIZE = 1000 }; if (width >= WORK_BUFFER_SIZE - EXTSIZ) { /* We have to use a special buffer. */ size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T); if (__libc_use_alloca (needed)) workend = (CHAR_T *) alloca (needed) + width + EXTSIZ; else { workstart = (CHAR_T *) malloc (needed); if (workstart == NULL) { done = -1; goto all_done; } workend = workstart + width + EXTSIZ; } }

引发malloc条件

width >= WORK_BUFFER_SIZE - EXTSIZ__libc_use_alloca (needed)==0

看看__libc_use_alloca()相关函数

/* Minimum size for a thread. We are free to choose a reasonable value. */ #define PTHREAD_STACK_MIN 16384 #define __MAX_ALLOCA_CUTOFF 65536 int __libc_use_alloca (size_t size) { return (__builtin_expect (size <= PTHREAD_STACK_MIN / 4, 1) || __builtin_expect (__libc_alloca_cutoff (size), 1)); } int __libc_alloca_cutoff (size_t size) { return size <= (MIN (__MAX_ALLOCA_CUTOFF, THREAD_GETMEM (THREAD_SELF, stackblock_size) / 4 /* The main thread, before the thread library is initialized, has zero in the stackblock_size element. Since it is the main thread we can assume the maximum available stack space. */ ?: __MAX_ALLOCA_CUTOFF * 4)); }

要使得返回值为0,那么要满足

__builtin_expect (size <= PTHREAD_STACK_MIN / 4, 1)==0__builtin_expect (__libc_alloca_cutoff (size), 1)==0

__builtin_expect 通常在if-else分支中使用,首先要明确一点就是 if (exp) 等价于 if (__builtin_expert(exp, x)), 与x的值无关。

三目运算符“?:” 中间省略的东东是与 “?”之前的数或者表达式完全一致。

即满足

size > PTHREAD_STACK_MIN / 4size > MIN(__MAX_ALLOCA_CUTOFF, THREAD_GETMEM(THREAD_SELF, stackblock_size) / 4 ?: __MAX_ALLOCA_CUTOFF * 4)

此时THREAD_GETMEM通常返回0,所以MIN函数最后的结果为__MAX_ALLOCA_CUTOFF即65536,所以size>65536此时也满足 PTHREAD_STACK_MIN / 4

所以对应size为65537即可引发malloc和free,首先劫持向_malooc_hook或者_free_hook的位置写入onegadget并确定合适width大小,然后利用printf函数格式化字符串漏洞(printf(buf),最后getshell

Use execveat to open a shell

execveat可以发挥与exceve相类似的作用

int execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags);

当参数pathname指向/bin/sh时,并且argv,envp,flags参数为0时,此时无论dirfd为何值,都可以getshell

对execveat参数的相关说明

If pathname is absolute, then dirfd is ignored.

参考Naetw大佬的GitHub上的总结

标签:

CTF-PWN-小tips由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“CTF-PWN-小tips