RTL(Return To Libc) 파라미터
1. RTL(Return To Libc)
▶ RTL 공격은 리눅스의 메모리보호기법 중 하나인 NX bit를 우회하기위해 사용하는 공격기법이다.
▶ 이 공격은 버퍼오버플로우를 통해 호출한 서브루틴의 리턴주소를 메모리에 위치한 함수의 주소로 변경한다.
(1) NX bit(Never eXecute bit)란?
▶ Windows 운영체제의 DEP(Data Execution Prevention)와 비슷하다.
▶ 즉, 메모리 내에 데이터 실행을 예방? 방지?한다.
※ 간단한 예로 공격자가 BOF를 일으켜서 shellcode를 작성하고 return address를 shellcode의 시작주소로 변경하였다.
NXbit 보호기법이 적용되지 않았다면 shellcode가 실행되지만 NXbit가 적용되었다면 스택 내에서 실행 시 Segmentation fault를 발생 후 종료된다.
▶ Arbitrary Code Execution(임의의 코드에 대한 실행)하는 공격을 막는다.
[1] Segmentation fault
▶ 허용되지 않은 메모리에 접근 시 메모리 오염을 막기위해 발생한다.
▶ 간단한 예로 읽기 전용으로 설정된 메모리에서 쓰거나 실행하려고 할 경우 발생한다.
(2) libc
▶ libc는 표준 C라이브러리 함수이다.
(3) 사용방법에 대한 이론
▶ 보통 Shell을 얻기위해 시스템해킹을 한다.
▶ 공유라이브러리는 여러프로그램이 같이 쓰기 때문에 메모리에 올라와있다.
▶ 그렇다면 return to libc라는 공격기법은 서브루틴의 리턴주소를 메모리에 위치한 함수의 주소로 변경한다???
▶ 공격기법의 이름에 맞게 표준 라이브러리에 있는 system()함수에 "/bin/sh"를 넣으면 되겠다는 생각이 든다.
▶ 그러나 내가 입력한 문자열은 안타깝게도 ASLR이라는 보호기법에 의해 프로그램이 실행될 때마다 주소가 변경된다.
▶ 그런데!!! system()함수는 내부적으로 execve()함수를 사용해서 만들어졌다.
▶ 헛... execve()함수는 "/bin/sh"를 통해서 동작을 한다????
▶ 이정도면 어느정도 느낌이 왔을 것이라고 생각을 한다.
▶ 세부설명을 하면 리턴주소에 system()함수의 주소를 덮어 씌운다.
▶ system()함수는 파라미터의 내용하는 함수이다. 즉 파라미터에 "/bin/sh"를 넣어야 한다.
▶ system()함수에 "/bin/sh"가 있다는 소식을 들었으므로 "/bin/sh"의 주소를 찾아서 파라미터로 넣어준다.
▶ 결과는?? 아래 시연에서 직접 확인해보려고 한다.
[1] ASLR(Address Space Layout Randomization)
▶ 메모리를 오염시키지 못하도록 힙, 스택, 라이브러리 등의 주소를 프로세스 주소공간에 랜덤하게 배치하는 것을 말한다.
▶ 즉, 실행할 때마다 변수나 프로세서의 메모리상의 위치(주소)가 변경된다.
[2] ASLR설정
▶ [sudo sysctl -w kernel.randomize_va_space=0] 명령어를 사용하면 ASLR이 해제된다.
▶ [sudo sysctl -w kernel.randomize_va_space=1] 명령어는 스택과 라이브러리에만 ASLR이 설정된다.
▶ [sudo sysctl -w kernel.randomize_va_space=2] 명령어는 힙과 스택과 라이브러리 모두에 ASLR이 설정된다.
▶ /proc는 메모리에 있는 커널과 현재 실행되고 있는 프로세스의 정보가 들어있다.
▶ 또한 메모리는 휘발성이기 때문에 재부팅 시 설정이 초기화된다.
(4) 시연 내용
[1] 시나리오
▶ 위 그림은 몇 번째주소에 무엇을 넣어야 하는지 이해를 돕기위해 버퍼를 입력하는 주소를 0으로 설정하였다.
▶ 이 메인함수는 프로그램 실행 시 인자에 문자열을 넣으면 출력해주는 프로그램이다.
▶ 먼저 왼쪽그림은 메인함수의 프롤로그 실행 후 스택공간을 확보 해놓은 상태이다.
▶ 그래서 문자열을 buf만큼 즉 256바이트만큼 넣고 SFP자리[0x100]에 아무 값이나 넣고 RET자리[0x104]에 system()함수의 주소로 덮어 씌워서 복귀 시 system()함수가 실행되도록 하려고 한다.
▶ 오른쪽그림은 system()함수가 실행된 이후이다.
▶ 왼쪽그림의 RET자리[0x104에 system()함수에서 프롤로그가 실행되어 SFP[0x104]로 덮여있다.
▶ 그 전에 system()함수가 끝나고 복귀할 주소로 RET[0x108]를 지정하고 system()함수에 들어왔다.
▶ 보통 함수의 인자는 ebp+8에 첫 번째 인자가 들어있다.
▶ 여기에 system()함수가 사용할 인자인 "/bin/sh"문자열의 주소를 넣어주면 된다.
▶ 위에 (3)에서 사용방법 이론에서 설명한 것과 같이 system()함수 내에서 "/bin/sh"의 주소를 찾을 예정이다.
▶ 그렇다면 공격코드는 어떻게 작성해야 할까?
▶ "A" * (256[buf]+4[SFP])+(4[system함수의 주소])+"B" * (4[NEW RET==ebp+4]), (4[system함수 내의 /bin/sh의 주소])
▶ 위의 내용이 이해가지 않으면 아래서 하나씩 진행하면서 확인해 보아도 좋다.
[2] 공격할 프로그램 소스
▶ 프로그램의 인자로 넣은 문자열을 출력하는 간단한 프로그램이다.
▶ if(argc != 2)로 많이 사용하지만 인자가 여러 개여도 무관하기 때문에 if(argc < 2)로 작성하였다.
▶ 인자 문제는 아래서 추가로 다룰 예정이다.
[3] system() 함수의 주소
▶ 메모리 보호기법 중 ???????????????????????????????????????????
▶ gdb -q는 도움말 보지 않고 실행하는 것이다.
▶ b *main은 main의 주소에 브레이크포인트를 설정한다.
▶ r은 run한다.
▶ print system은 system함수의 주소를 출력한다.
▶ 빨간네모를 보면 system함수의 주소가 0xf7e1cd10인 것을 알 수 있다.
[4] system()함수 내에 "/bin/sh"의 주소찾는 소스
▶ 위와 같이 long type의 address변수에 system함수의 주소를 작성한다.
▶ 반복문을 돌려 address변수에 있는 주소를 하나씩 증가시키면서 8바이트("/bin/sh\00")만큼 비교한다.
[5] "/bin/sh"의 주소 확인
▶ system()함수 내에 "/bin/sh"문자열이 있는 주소는 0xf7f5b8cf이다.
[6] ASLR 해제
▶ 그러나 계속 프로그램을 실행해보면 "/bin/sh"의 주소가 다르게 나온다.
▶ 그래서 ASLR을 우회하는게 목적이 아니기 때문에...
▶ [sudo sysctl -w kernel.randomize_va_space=0] 명령어로 ASLR을 해제한다.
[7] gdb 실행
▶ set disassembly-flavor intel은 인텔문법으로 표기한다.
▶ disas *main은 main함수를 disassemble 한다.
▶ 문자열배열 256바이트만큼을 지역변수로 선언했기 때문에 프롤로그 후 0x100만큼 스택공간을 확보하였다.
▶ 스택공간 확보 전인 [main+3] 그리고 에필로그 전인 [main+79]에 브레이크포인트를 설정할 예정이다.
▶ eax에 [ebp-0x100]의 주소를 넣는데 이것이 복사할 문자열의 시작주소이다.
▶ 그렇다면??? 여기서 [ebp-0x100] 주소가 입력할 버퍼의 주소임을 알 수 있다.
[8] 브레이크포인트 설정
▶ 브레이크포인트를 2개 설정한다.
▶ c는 다음 브레이크포인트까지 이동한다.
▶ d는 모든 브레이크 포인트를 지우고 d 1과 같이 입력 시 1번 브레이크포인트가 지워진다.
▶ i b로 브레이크포인트 설정목록을 확인할 수 있다.
▶ r $(python -c 'print("A"*0x100+"B"*4+"\x10\xcd\xe1\xf7"+"C"*4+"\xcf\xb8\xf5\xf7")') 를 입력한다.
▶ 위 공격코드는 아래서 추가로 설명할 예정이다.
[9] 첫 번째 브레이크포인트 확인
▶ i r 은 레지스터의 값을 확인할 수 있다.
▶ p $esp 는 esp레지스터의 값을 확인할 수 있다.
▶ 스택공간을 ebp에서 0x100만큼 확보한 것을 알 수 있다.
[10] 두 번째 브레이크포인트 확인
▶ 브레이크포인트가 에필로그 전에 걸려있기 때문에 함수는 다 실행한 이후일 것이다.
▶ 결과적으로 내가 입력한 내용이 다 출력되었고 main함수를 종료하고 return address로 돌아갈 것이다.
[11] 스택포인터 확인
▶ 아까 확인한 0xffffcf48~0xffffd047(0x100개의 바이트)까지는 아래 왼쪽그림의 buf[256]자리이며 전부 A로 채워졌다.
▶ 0xffffd048주소 아래 왼쪽그림의 SFP자리이며 BBBB(4개의 바이트)가 채워졌다.
▶ 0xffffd04C주소 아래 왼쪽그림의 RET자리이며 system()함수의 주소(0xf7e1cd10) 4바이트가 채워졌다.
▶ 0xffffd050주소 아래 왼쪽그림에 있는 para1자리이며 이곳은 CCCC(4바이트)가 채워졌다.
▶ 0xffffd054주소 아래 왼쪽그림에 있는 para2자리이며 "/bin/sh"의 주소(0xf7f5d8cf)로 채워졌다.
▶ 오른쪽그림은 system()함수가 실행된 이후이다.
▶ 결과적으로 system("/bin/sh")를 실행한 것이며 이전 그림에서 $(Shell)이 떨어졌으며 ls등이 동작하는 것을 확인할 수 있다.
[12] 프로그램에 인자넣고 실행
▶ 위와 같이 넣고 실행할 수 있다.
▶ 다른 쉘도 실행이 가능하며 system()함수의 return address를 지정해주지 않아서 종료 시 세그먼테이션 오류가 발생한다.
3. 시연 영상
▶ 위에서 다룬 내용을 직접 시연한 영상이다.
▶ 참고!! 브레이크포인트 걸 때 main+99로 잘못 걸어서 오류가 있기는 하지만 실행은 된다.
4. 깨달음1
나는 아니지만 같이 공부하는 사람에게 질문을 받았다.
에러가 발생하여 동일한 환경(OS, Network)에서 동일한 코드를 가지고 다시 진행을 하였다.
그래도 문제가 있었다.
문제는??????????????????????????
system함수 내에 "/bin/sh"의 주소를 찾았고 주소를 맞춰서 넣었고 확인도 했다.
그러나???????????????????
주소가 정확히 들어가지 않는다.
이 문제로 이것저것 설정바꿔보고 다시 컴파일해보고 보호기법 다시확인하고...
그렇다면 이유가 무엇일까???
"/bin/sh"의 주소가 [0xf7f520b2]인가 그랬다...
결론부터 말하면 주소에 16진수 20이 들어가 있었고 이것은 아스키코드로 보면 space이다.
그런데 이것은 사전에 전조증상?이 있었다.
무언가 이상한 느낌은 메인 프로그램의 if문에서 (argc != 2)로 하면 프로그램이 실행이 되지 않았다.
argc를 출력해보면 3으로 표시되었다.
드는 생각은 "/bin/sh"라서 입력이 막혀있나? 필터링을하나? 이랬지만 다른 환경에서는 문제가 없었다...
결국 space가 있었기에 뒤의 내용은 다음 인자의 값으로 들어간 것이다.
또한 BOF 공격으로 채워넣어도 정확한 주소가 박히지 않았던 것이다.
그렇다면 이 주소를 바꿔야 하는데... ASLR을 해제하여 항상 동일한 곳에 위치해서 재부팅 등의 문제로 해결되지 않는다.
실행권한이 없기 때문에 주소를 shift하는 코드를 넣을 수도 없고... 내 능력 밖인 것 같다.
이 부분은 추후에 알게되면 기술할 예정이고 그래서!!!
환경변수에 "/bin/sh"를 넣어보기로 했다.
이 부분은 다음 글에서 다루는게 좋을 듯 싶다.
5. 깨달음2
또하나.. 파이썬2버전을 사용할 때랑 파이썬3버전을 사용할 때랑 문법적인 문제는 없다.
직접 출력을 해보면 정상적으로 출력되는 것도 확인이 된다.
그러나 (gdb)에서 넣고 확인을 해보았다.
파이썬3의 경우 내가 적은 값이 주소에 정상적으로 들어가지 않는다..
왜인지 알수는 없지만 같은 코드로 프로그램을 실행시켜보면??
위 그림과 같이 빨간 네모가 있는 부분에서 BBBB의 왼쪽이 리턴주소이고 오른쪽이 "/bin/sh"의 주소이다.
자세히보면 출력결과가 다르다.
아스키코드는 127까지밖에 표현을 못하기 때문에 이상한 값이 출력되는 것이고 알 수 없는 문자는 환경변수에 따라 표현이 다른 것 같다.
어쨋든 파이썬2버전은 동작하고 3버전은 동작하지 않는지 원인은 찾지 못했다.ㅠㅜ
이 부분도 추후에 알게되면 기술할 예정이다.
'Try Attack > System Hacking[basic]' 카테고리의 다른 글
Windows에서 Shellcode 작성하기 (0) | 2019.07.27 |
---|---|
입력방법에 따른 python 공격코드 작성법 (0) | 2019.01.19 |
RTL(Return To Libc) 입력 (0) | 2019.01.11 |
Overflow - 2 (0) | 2018.05.24 |
Overflow - 1 (0) | 2018.05.20 |
댓글