PWN1

ROP(Return Oriented Programming) 기법을 활용한 exploit

1. 개요

시스템 해킹에서는 이 기법을 아는 사람과 모르는 사람의 exploit 수준이 달라진다. 또한, 시스템 해킹의 기본적인 기법이므로 꼭 알아야 하는 내용이기도 하다. 그럼 ROP 기법을 활용하기 위한 준비과정을 거쳐보자.

 

<Pwntool>

앞에서 설명했지만, '우리가 pwntool을 왜 사용해야 할까?'라는 질문에는 어떤 답이 있을까?

시스템 해킹을 위해서? 해킹 문제를 편하게 풀기 위해서? 물론 답은 될 수 있다. 하지만, 근본적으로 생각해보자.

LOB같은 Red Hat Linux에서는 pwntool을 지원하지 않는다. 왜냐하면 말 그대로 기초에서는 해킹 기법의 기본 이해가 중요하기 때문이다. 그런데 이제부터는 시스템 해킹의 효율성을 고려해야 한다. 만약 복잡한 기법을 사용한다면 내가 실행시키고 싶은 명령을 쉘에 한줄로 다쓰는 것은 너무나도 벅차다. 또한, 자신이 쓴 exploit 코드의 이해도가 떨어질 수 있다는 단점이 있다.

그래서 등장한 것이 Python을 기반으로 한 Pwntool이다. 우리는 pwntool을 설치하고 python에서 import라는 과정만 거치면 누구나 이 툴을 사용할 수 있다. 또한 내가 원하는 변수를 만들고 원래 있던 라이브러리에서 내가 원하는 주소를 가져올 수 있고, 코드가 누구나 이해가도록 작성할 수 있다는 장점이 있다. 그래서 Pwntool을 사용하라는 것이다. ROP를 이해하고 쉽게 짜기 위해서는 Pwntool이 필수다.

 

<ROP 기법이 필요한 이유>

앞에서 RTL 기법을 배웠는데, NX bit 보호 기법을 우회하기 위한 목적을 가지고 있었다. 그러나 만약 ASLR이 걸려버린다면 system 주소와 "/bin/sh"의 주소를 맞게 적용했더라도 주소가 랜덤으로 바뀌어 그 기법을 사용하지 못할 것이다. 이럴 때 ROP는 바이너리에 ASLR과 NX bit 기법이 적용되어 있을 때 확실하게 exploit할 수 있다. 그것이 ROP를 사용하는 이유이다.

 

<함수의 PLT, GOT>

우리가 컴파일을 할 때 함수를 사용하기 위해서는 C에서는 #include<stdio.h>같은 명령어를 사용한다. 이 의미는 stdio.h라는 헤더파일을 외부에서 가져와 사용하겠다는 뜻이다. 프로그램도 마찬가지다. 컴파일을 한 후 만들어지는 exe 파일에 모든 정보가 들어있는 경우(static linking) 함수의 주소를 쉽게 알아낼 수 있지만, 외부 라이브러리를 불러오는 경우(Dinamic linking) 함수 호출을 위해 주소를 구하여 접근하는 방법이 필요하다. 그래서 등장한 것이 PLT와 GOT이다. 만약 내가 함수를 호출하면 먼저 PLT를 참조하게 된다. PLT는 어셈블리어 "jmp got" 즉, got를 참조하겠다는 뜻이다. GOT는 내가 불렀던 함수의 실제 주소가 담겨져 있는 곳이다. 순서를 정리하면 다음과 같다.

함수 호출 -> 그 함수의 PLT 참조 -> 그 함수의 GOT 참조 -> GOT에 들어 있는 실제 주소에 접근해 함수를 호출

그러면 이 배경지식을 바탕으로 ROP 기법의 원리를 알아보도록 하자.

 

2. exploit의 원리

우선 이 기법이 일어나는 순서를 정리하자면 다음과 같다.

1 read_got에 있는 실제 read 함수의 주소를 찾는다.
2 system 함수의 read 함수와의 offset을 찾은 뒤, system 함수의 실제 주소를 알아낸다.
3 read 함수로 bss(전역 변수를 저장할 수 있는 곳) 영역에 '/bin/sh' 문자열을 저장한다.
4 read 함수로 write_got에 들어있는 주소를 system 함수의 실제 주소로 덮어씌운다.(got overwriting)
5 bss 영역의 '/bin/sh' 문자열의 주소를 인자로 가지는 system 함수를 write_plt를 호출하여 실행한다.

되게 복잡한 것 같지만, 알고보면 당연한 이야기이다. 차근차근 설명을 해보겠다.

 

1. read_got에 있는 실제 read 함수의 주소를 찾는다.

우리의 목표는 system 함수에 인자 "/bin/sh"을 넣어 쉘을 따는 것이다. 그러기 위해서는 원래 있었던 함수를 활용하여 내가 사용하고 싶은 함수의 주소를 알아내야 한다. 따라서 필자는 read 함수의 주소를 먼저 찾아내었다.

2. system 함수의 read 함수와의 offset을 찾은 뒤, system 함수의 실제 주소를 알아낸다.

read 함수의 실제 주소를 알아내었으므로, read 함수와 system 함수 사이의 거리를 측정하여 그 주소만큼 더하거나 빼면 system 함수의 주소를 알아낼 수 있다. 예를 들어 read-system=10이고 read 함수의 실제 주소는 30이라면 system함수의 주소는 read-10인 20이 되는 것이다.

3. read 함수로 bss(전역 변수를 저장할 수 있는 곳) 영역에 '/bin/sh' 문자열을 저장한다.

우선 왜 bss 영역을 사용해야만 하는지 알아야 한다. ROP의 목적은 ASLR과 NX bit 보호 기법이 걸려 있는 바이너리에서 해킹을 성공시키는 것이다. 이 중 ASLR은 스택과 힙, 라이브러리 영역의 주소를 랜덤화시키는 것이기 때문에 bss 영역에는 쓰기 권한도 있으면서 접근도 할 수 있는 것이다.(bss 영역은 stack 영역 안에 포함되어 있지 않다.) system 함수의 인자에 문자열을 넣기 위해 bss에 저장한 뒤,그 주소를 인자로 불러올 것이다.

4. read 함수로 write_got에 들어있는 주소를 system 함수의 실제 주소로 덮어씌운다.

4번이 ROP 기법에서 필수적인 과정이다. 우리가 알고 있는 것은 write_got,plt, read_got,plt가 있다. 그런데 우리는 앞에서 read 함수를 활용하고 있기 때문에 write의 got를 read 함수로 덮어씌울 것이다. 방법은 간단하다. 명령어 read(0,write_got,4); 명령어를 실행시켜 사용자가 system의 실제 주소를 입력하면 된다. 이렇게 된다면 got와 plt의 참조에 의해 write_plt를 호출함으로써 system 함수를 실행한 것과 같은 효과를 낼 것이다.

5. bss 영역의 '/bin/sh' 문자열의 주소를 인자로 가지는 system 함수를 write_plt를 호출하여 실행한다.

계속 이야기했던 것이다. 마무리는 system 함수의 인자 보내는 방법을 통하여 이루어진다.

exploit은 pwntool을 이용하여 코드를 작성하는데, 이 원리를 이해하고 어셈블리어를 정확히 읽을 수 있다면 코드 짜는 데에는 문제가 없을 것이다.

댓글

댓글 본문
버전 관리
2019STEALTH
현재 버전
선택 버전
graphittie 자세히 보기