본문 바로가기

security/pwn

[pwn] 로되리안 해결하기

항상 pwnalble 문제를 풀 때 고질적인 문제 중 하나였다. 로컬에서는 분명 쉘이 따지는데 remote로 서버에 보냈을 때 쉘이 따지지 않는 상황들이 있다. 이럴 때 대부분은 libc 파일의 버전 문제이다.

 

 

Q. libc의 버전이 다른게 왜 libc 속 함수의 offset에 영향을 주는가?

A. libc 파일을 업데이트하면서 코드가 길어지거나 짧아지면서 offset에 변화가 생긴다고 한다.

 

 

최근 ctf나 워게임에서 왠만하면 libc버전 문제 때문에 Dockerfile만 주거나 바이너리와 함께 준다. 이전까지는 pwnable 한 문제를 풀 때마다 Docker를 하나 빌드해서 안에서 또 쓸 도구들 설치하는게 말이 안된다고 생각했는데 그냥 Dockerfile에 필요한 도구들 명령어로 박아놓으면 된다.

 

 

Docker 빌드는 최후수단으로 써야 할 것으로 보인다. 그래서 내가 선택한 방법은 문제를 풀 때 libc 파일을 줄 경우 사용 vm에 가져와서 바이너리와 같은 경로에 넣고 ELF('./libc.so.6')로 offset을 빼오거나

 

 

readelf -s libc.so.6 | grep " system@"

strings -tx ./libc.so.6 | grep /bin/sh

 

 

위의 명령어처럼 직접 뽑아오면 된다. 쉘에서 사용하는 명령어이다. libc.so.6에서 sytem 함수의 주소를 뽑아오고 /bin/sh 문자열 주소를 찾는 명령어이다.

 

 

아니면 Docker를 빌드하되 빌드 후 libc 파일을 빼내올 수도 있다.

 

 

docker cp [컨테이너 id]:[파일 저장할 로컬 경로] [파일 이름]

docker cp [파일 이름] [컨테이너 id]:[파일 저장할 로컬 경로]

ex) docker cp prob 68:/DH/pwn/gaia

 

 

위의 명령어는 docker에서 os로 libc를 추출하는 명령어이고 밑에처럼 반대로 할 경우 os에서 docker로 파일을 전송할 수 있다. 이때 컨테이너 id는 구분할 수 있게 2글자나 몇글자만 써도 가능하다. docker를 빌드하고 필요한 도구들을 설치하고 푸는 것 보다 이렇게 libc만 빼내와서 로컬에서 문제를 푸는 방법도 있다.

 

 

만약 문제에서 주어진 libc가 사용이 안된다면 아래와 같이 명령어로 libc를 적용하는 방법도 있다.

 

 

p = process('./prob',env={"LD_PRELOAD":"./libc.so.6"})

 

 

다음과 같이 process에서 LD_PRELOAD 환경변수로 libc를 지정하는 방식이다.

이렇게 libc를 강제로 붙이고 실행하면서 process debugging으로 libc들 주소 맞는지 하나씩 확인하는게 상당히 효과가 좋다.

 

 

gdb.attach(p)

 

 

이때 매번 pid로 디버거를 붙일경우 귀찮으므로 위의 명령어로 실행하면 편리하다.

 

 

또한 마지막으로 함수의 이름과 주소 뒤 3자리로 libc 버전을 알고 다운 받을 수 있는 libc database 사이트가 있다. libc 버전을 알 수 있는 libc database라는 open source가 있지만 귀찮으니 사이트가 낫다. 그리고 정확도는 잘 모르겠어서 최후에 사용하는 것이 좋다.

 

 

https://libc.rip/

 

libc-database

 

libc.rip