Leviathan Level 2 입니다.
약간의 리버싱과 command Injection 기법이 사용된 문제입니다.
일단 저번 레벨과 비슷하게, SetUID비트가 설정된 실행파일이 하나 있네요.
소유자는 leviathan3 이므로, 저 파일을 실행하는 동안 우리는 leviathan3의 권한을 얻게 됩니다.
일단 인자 없이 실행해 보았는데요, "File Printer"라는 문자열과 함께
./printfile [파일명] 형식으로 실행하라고 합니다.
음.... 출력이 안되는군요. leviathan3 파일에 대한 어떤 제한이라도 걸려있는 걸까요?
리버싱을 통해 확인해 봅시다.
저는 radare2 프로그램을 사용하였구요, 이것은 main함수의 구조입니다.
가장 처음 나오는 조건문입니다.
9212 부분에서 cmp dword [eax], 1 명령어를 실행하는데요, 여기서 [eax] 는 우리가 입력한 인자를 나타냅니다.
입력한 인자와 1을 비교하는 겁니다.
바로 다음 줄 jg는, 이전 cmp문에서 첫번째 인자가 더 클 경우 점프하는 명령입니다. 우리가 인자를 입력했다면 1보다는 더 클 테니, 결국은 "인자를 입력했는지 확인" 하는 부분이라고 할 수 있습니다.
인자를 입력하지 않았다면, 밑에 주석으로 보이는 File Printer, Usage: ./printfile filename 등등 문자열이 출력됩니다.
우리가 인자를 입력했을 경우 나타나는 진행입니다.
925b 부분에서 access 함수를 이용하고 있습니다. 인자는 우리가 입력한 파일경로와 "4" 입니다. (바로위 push부분)
push는 스택에 값을 저장하는 명령어인데, 마지막에 들어온 게 가장 빨리 나가게 되므로 첫번째 인자가 eax(입력한 경로), 두번째 인자가 4인 겁니다.
access함수는 파일에 대한 권한 등을 확인하는 함수입니다. 두번째 인자 "4"는 파일의 존재 유무를 확인하겠다는 뜻입니다. ( 읽기, 쓰기, 실행, 존재유무 4가지 확인이 가능 )
따라서, "우리가 입력한 파일이 존재하는지 확인" 하는 것 입니다.
그리고 존재한다면 0을, 존재하지 않는다면 -1을 반환합니다.
이어서 9263, 9265부분 test명령과 je 점프문이 있는데요
파일이 존재한다면 je점프문을 타고 점프를 하게 됩니다.
존재하지 않는다면, 바로 아래에 보이는 You cant have that file 문자열을 출력하게 됩니다.
위 두가지 조건을 통과한 후 (인자를 입력했고, 존재하는 파일일 경우) 진행되는 구조입니다.
929b부분 snprintf 함수가 눈에 띄는데요. 쉽게 말해서 문자열을 만드는 함수입니다.
위에 push 명령들이 하나씩 snprintf 함수의 인자가 되므로, 이런 모습을 띕니다.
snprintf(char *s, 0x1ff, "bin/cat %s", [파일경로])
-> 버퍼를 0x1ff 크기만큼 할당해서, "/bin/cat 파일경로" 문자열을 저장
그리고 아래 geteuid, setreuid 함수들은 leviathan3의 권한을 부여하는 과정이구요. (leviathan3 소유의 SetUID 프로그램이니까요)
최종적으로 92c6부분 system 함수에서, 위 snprintf 에서 만들어둔 문자열을 인자로 사용해서 명령어를 실행합니다.
정리하자면, "인자를 입력했고 그 파일이 존재하는 파일일 경우, leviathan3의 권한으로 해당 파일을 읽어온다" 가 되겠습니다.
그러나 우리는 처음에 /etc/leviathan_pass/leviathan3 입력으로 password 획득에 실패했죠.
따라서 조금 다른 방식으로 도전해야 합니다.
자, 리눅스에선 ;(세미콜론) 을 이용하면 명령어 한 줄에 두개의 명령을 실행할 수 있습니다.
이걸 이용해서 password를 획득해 보자고요.
./printfile /etc/passwd;sh
리눅스 시스템이라면 꼭 존재하는 passwd 파일을 읽어오도록 하고, 이어서 쉘을 실행하도록 했습니다.
그러나 권한이 상승되지 않았습니다.
./printfile /etc/passwd 까지가 하나의 명령어, 이후 sh는 별개의 명령어 였으므로
passwd는 leviathan3의 권한으로 읽어왔지만 sh는 그러지 못한 것 같습니다.
그렇다면 "(더블쿼터) 로 묶어서 전달해 봅시다.
음....
더블쿼터로 묶어서 전달했더니, 이번엔 passwd를 읽지도 못하고 쉘도 실행되지 않았습니다.
저는 솔직히 이러면 될 줄 알았는데, 조금 당황했습니다.
침착하게 다시 생각해보니까, 원인이 있더라구요.
아까 리버싱 과정에서, 조건문 두개를 통과해야 한다고 했었죠?
1. 인자가 존재해야 함
2. 존재하는 파일이어야 함(access 함수)
1번 조건은 통과했으나, 2번 조건에서 막힌 겁니다.
access함수는 명령어 실행 함수가 아닙니다. 단순히 문자열을 입력받고, 그 파일이 존재하는지만 체크합니다. ;(세미콜론) 을 인식하지도 못해요. 결국 명령어 실행은, 2번 조건을 통과한 후 마지막 system() 함수에서 이루어집니다.
우리가 뭘 입력했죠? "/etc/passwd;sh" 입니다. /etc/passwd;sh 라는 파일이 있나요? 없습니다.
그래서 2번째 조건에서 막힌 것입니다.
따라서 우리가 할 일은, 파일명 자체를 명령어로 만들어서 전달하면 됩니다.
실제로 존재하는 파일이니 2번 조건은 통과할 테고, 마지막 system함수에서 ;(세미콜론)이 인식되어 쉘이 실행되겠죠.
홈디렉터리엔 쓰기권한이 없으므로 /tmp 디렉터리에서 진행합니다.
파일 내용은 아무거나 쓰시고, 파일명도 아무거나 쓰신 후 뒤에 ;sh 를 붙여서 파일 하나를 만들어줍시다.
파일명은 반드시 "(더블쿼터) 로 묶어주셔야 합니다.
이 파일은 저렇게 더블쿼터로 묶어서 그냥 읽어주면 정상적으로 내용을 출력하지만,
;(세미콜론)이 인식되도록 cat 명령어에 담았을 경우엔, 무섭게도 쉘이 실행되어버리죠.
이걸 printfile 프로그램에게 읽으라고 시키면 됩니다.
아까 말씀드렸듯이 더블쿼터로 묶지 않으면 세미콜론 뒤 명령은 별개의 명령으로 인식하기 때문에, 반드시 더블쿼터로 묶어주어야 합니다.
자, 이렇게 leviathan3의 쉘을 획득했습니다. 이제 password 파일을 읽으면 됩니다.
이렇게 password를 획득할 수 있었습니다.
=====================
그리고 구글링하다가 한가지 방법을 더 알아냈습니다.
심볼릭 링크를 이용하는 것 입니다.
파일 3개를 만듭니다.
test1 (빈 파일)
test1 test2 (빈 파일, 공백이 포함된 하나의 파일명임)
test2 ( leviathan3에 대한 심볼릭 링크 파일 )
그리고 공백이 포함된 파일을 더블쿼터로 묶어 전달합니다.
그러면 password를 획득할 수 있습니다.
원리가 무엇이냐면,
1번 조건 : 인자가 존재하므로 통과
2번 조건(access함수) : "test1 test2" 한개의 파일로 인식되어 통과 (대충 만든 빈 파일이므로 통과됨)
system함수 : 그렇게 전달된 test1 test2는 각각 다른 파일로 인식되어 test2의 심볼릭 링크로 password를 읽어옴
이렇게 되는 것입니다. 두 함수에서 파일명을 받아들이는 형태가 다른 이유는 아마도...
-> system함수에서 선처리 구문으로 "더블쿼터를 입력해두지 않았음
-> 따라서 access함수에서 system함수로 전달되는 과정에서 "더블쿼터가 소실되어 두개의 파일로 인식
이것 아닐까요? 확실한 건 아닙니다만 감히 예상해봅니다.
====================
맨 처음 printfile 프로그램으로 /etc/leviathan_pass/leviathan3 파일을 바로 읽으려 했을 때 안되었던 부분 말입니다.
리버싱으로 이러한 시도를 막는 부분이 어디인지 찾아보려고 했는데, 찾지 못해서 찝찝하네요.
혹시 access 함수에서 인자 "4"를 썼을 때 파일의 존재 유무만을 확인하는 게 아니라, 해당 파일에 대한 권한이 없다면 0이 아닌 값을 출력하는 걸까요? ( 이거일 것 같긴 합니다 )
근데 그거라면 굳이 인자 "4"만 쓸 이유가..
'Hacking > [System]Leviathan' 카테고리의 다른 글
OverTheWire [Leviathan Level 5] (0) | 2023.05.24 |
---|---|
OverTheWire [Leviathan Level 4] (0) | 2023.05.24 |
OverTheWire [Leviathan Level 3] (0) | 2023.05.24 |
OverTheWire [Leviathan Level 1] (0) | 2023.04.09 |
OverTheWire [Leviathan Level 0] (0) | 2023.04.05 |