OverTheWire Behemoth 

[ level1 -> level2 ]


behemoth 레벨1입니다. narnia와 달리 소스코드가 주어지지 않아 gdb로만 분석을 해야하는 불편함이 있습니다.

바로 확인 해 보도록 합시다. 



/behemoth 디렉토리로 이동하여 behemoth1 바이너리를 실행시켰을 때의 결과입니다.

패스워드를 요구하며, 패스워드에 부합하지 않을 시 "Authentication failure." 라는 메시지를 내뱉고 프로그램을 종료시키는 듯 합니다.

소스코드가 없으니 gdb를 이용해서 main 부분을 확인 해 보도록 하죠.



main 함수를 보니 인증 부분 자체가 없습니다. 그냥 gets 함수로 값을 입력받고, puts 함수로 오류 메시지를 내뱉고 종료시키네요.

인증이고 뭐고 BOF를 이용해서 쉘을 띄우라는 의도인 것 같습니다.

BOF를 위해 입력받은 값이 어디로 들어가는 지 확인 해 보았습니다.



ebp-0x50 부분을 확인 해 보니, 0xffffd5fe부터 값이 차례로 들어가는 것을 알 수 있었습니다.

이제 main 함수의 return address를 찾아야 하는데, 하나씩 확인 해 보니 0xf7e3da63 부분이 __libc_start_main 함수로 이어지는 것을 알 수 있었습니다.

다음 그림을 보도록 하죠.



__libc_start_main 함수에서 main 함수를 호출 한 이후 모든 루틴을 끝냈을 때 다시 __libc_start_main 함수로 돌아와 exit함수를 호출하며 프로그램을 종료시키는 형태입니다.

이를 return address로 간주하고 풀이하도록 하겠습니다.

이 return address와 buffer 사이의 간격이 79byte이기 때문에 문자 'A'를 총 79번 입력 한 이후 쉘코드가 있는 주소를 적어주면 쉘이 띄워질 것입니다.



gdb상에서 A를 79번 입력하기 위해 임시로 'a'라는 파일을 만들어 주었습니다.



gdb로 실행시켜 해당 return 값의 직전까지 값이 들어가는 것을 확인한 결과 화면입니다.

다음으로, 쉘코드를 담을 공간을 확보하는 것인데, 첫번째 방법으로는 우리가 A로 집어 넣은 버퍼 공간에 쉘코드를 집어 넣는 방법이 있으며, 두번째는 환경변수를 이용하여 쉘코드를 입력 한 후, 그 주소를 ret 값에 덮어 씌우는 방식입니다. 저는 두번째 방법인 환경변수를 이용하는 공격 기법으로 풀이하도록 하겠습니다.



export 명령어를 이용해 EGG라는 이름의 환경변수를 만들어주고, 그 값으로 0x90을 100번 반복하고 그 뒤에 쉘코드를 입력해주었습니다. 0x90을 여러번 입력 해 주는 이유는 0x90이 NOP(아무 행동도 하지 않고 다음에 나오는 명령을 실행)의 의미이기 때문에 주소의 오차 범위를 줄임과 동시에, 스택 상에서 환경변수의 주소를 찾기 용이하게 하기 위해 입력 해 주었습니다.



gdb에서 스택을 확인하였더니 그림과 같이 0x90의 반복되는 부분을 확인 할 수 있었습니다. 이 중 아무 주소나 선택하면 되는데, 저는 0xffffd8b8을 리턴 주소로 선택하여 exploit 하도록 하겠습니다.



위에서 구한 EGG 환경변수의 주소를 ret 값에 덮어 씌워주면 behemoth2 권한의 쉘을 띄울 수 있습니다.





'Wargame > Behemoth:OverTheWire' 카테고리의 다른 글

OverTheWire Behemoth [level2 -> level3]  (0) 2016.04.29
OverTheWire Behemoth [level0 -> level1]  (0) 2016.04.07

OverTheWire Behemoth 

[level0 -> level1]



OverTheWire Behemoth의 첫 번째 문제입니다.




다른 난이도와 동일하게 /behemoth 디렉토리에 문제가 자리 해 있습니다. 하지만 다른 난이도와 다른 점이 있다면 원본 소스코드를 지원 해 주지 않는 다는 점이 있겠네요. 

현재 behemoth0 계정이므로 behemoth0 바이너리를 실행시켜보았습니다. 패스워드를 요구하며, 패스워드가 맞지 않을 경우 Access denied.. 를 출력하며 프로그램을 종료시킵니다. 만약 지정된 패스워드를 입력 할 경우 쉘이 떠지는 방식인 것 같습니다.

gdb로 루틴을 확인 해 보도록 하겠습니다.



main 함수를 disassemble 한 결과, 중간에 strcmp 함수(두 번째 네모 박스)가 있는 것을 확인 할 수 있습니다. esp+0x1f와 esp+0x2b를 비교하는데, esp+0x2b는 위의 scanf에서 받아오는 문자열이 저장 된 위치입니다. 그리고 esp+0x1f는 시작 부분에서 0x475e4b4f에 있는 문자열을 저장 해 놓은 위치입니다. 이 문자열은 암호화 된 상태로 입력되었으며, 첫 번째 네모 박스에 있는 memfrob 함수를 이용하여 복호화합니다. 다음은 memfrob 함수의 원형입니다.



 void * memfrob(void *s, size_t n); 


문자열 s의 각 요소를 '42'와 XOR 하는 함수입니다. 무조건 '42'와 XOR 하기 때문에 암호화라기 보다는 문자열을 숨기기 위한 임시 방편이라고 생각 하는 편이 좋을 것 같습니다. 처음 memfrob을 사용하면 원래의 문자열을 xor 하여 암호화 하고, 다시 memfrob 함수를 사용하면 복호화 되는 방식입니다. 위에서 설명 했듯이, esp+0x1f에 입력 되어 있는 문자열은 memfrob함수로 이미 암호화 된 값이 들어가 있기 때문에 첫 번째 네모 박스에서 복호화를 진행하는 과정이라고 생각하면 되겠습니다.

이 복호화 된 값과 입력 받은 값을 strcmp 하여 값이 같지 않으면 main+195로 점프하여 프로그램을 종료시키네요. 분기하지 않았을 경우는 main+188(세 번째 네모 박스)에서 execl을 호출하는 것으로 보아 쉘을 떨어뜨려주는 것 같습니다.

그럼 memfrob 함수를 호출 한 이후에 breakpoint를 걸어 esp+0x1f에 들어있는 문자열을 확인 해 보도록 하겠습니다.



저는 main+129의 위치에 breakpoint를 걸어주고 값을 확인 했습니다.

해당 위치에는 "eatmyshorts"라는 문자열이 들어 가 있었습니다. 이 문자열을 바이너리를 실행 했을 때 패스워드 부분에 입력 해 주면 쉘이 떨어 질 것 같습니다.



- password : eatmyshorts




예상대로 Access granted.. 라는 메시지를 띄워주면서 쉘을 뱉어냈습니다. behemoth1 권한을 가지고 해당 레벨로 가는 패스워드를 얻어냈습니다.


'Wargame > Behemoth:OverTheWire' 카테고리의 다른 글

OverTheWire Behemoth [level2 -> level3]  (0) 2016.04.29
OverTheWire Behemoth [level1 -> level2]  (0) 2016.04.28

OverTheWire Narnia 

[ level9 ]



OverTheWire Narnia 클리어


OverTheWire Narnia 

[ level8 -> level9 ]



오랜만에 풀이하는 OverTheWire 문제입니다. 이전에 하던 Narnia 난이도의 마지막 문제를 풀이했는데요, 간만헤 하는 시스템해킹인지라 감이 떨어졌는지 한 문제 푸는데만 하루가 꼬박 걸렸습니다. 문제 보도록 하죠.




소스입니다. 메인 함수를 확인 해 보니 argv[1]을 func 함수로 보내주네요. func 함수는 blah 포인터 변수에 받아온 argv[i]의 값을 넣어 준 후 20의 크기를 가진 bok이라는 배열을 선언합니다. 그리고 memset 함수를 이용하여 bok의 사이즈(20)만큼 NULL로 초기화를 시켜줍니다. 그 후 가장 중요한 부분입니다. 빨간 네모 박스 쳐져 있는 부분인데, blah[i]가 NULL이 나올 때 까지 bok 배열에 argv[1]의 값을 하나씩 집어 넣어줍니다. 여기서 ret 값을 덮어 씌우는 BOF 취약점이 발생 할 수 있습니다. 

다음은 문제 풀이 할 때 표기했던 내용인데, func 함수를 disassemble 한 어셈블리 코드입니다. 보기 쉽게 주석으로 의미를 적어두었습니다.





참고 삼아서 보면 될 것 같습니다.

다음으로 gdb로 직접 돌려보면서 값을 확인 해 보도록 하겠습니다.



argv[1]의 값으로 A를 총 20개 넣었을 때의 결과입니다. func함수의 어셈블리 코드를 확인 했을 때 ebp-0x20의 위치가 bok 변수의 위치라고 명시 되어 있었으니, continue를 하면서 값을 확인 해 보았습니다.





그리고 총 20번을 돌았을 때의 결과입니다. 20바이트 직후에 나타나는 4바이트는 바로 blah 변수입니다. 어셈블리 코드를 확인 해 보면 ebp-0xc의 위치에 blah가 위치해 있다는 것을 확인 할 수 있는데, 0x20-0xc = 32-12 = 20 입니다. 그래서 20바이트 직후에 blah가 위치해있는데, 이로 인해 생기는 문제는 다음 그림을 통해 확인해보도록 하겠습니다.



위에서 설명했던 문제가 바로 빨간 박스 쳐진 부분입니다. 첫 번째와 두 번째 박스를 확인 해 보면 0xffffd8a2였던 blah 주소 값의 마지막 부분이 0x41(A)로 덮어씌워지는 것을 알 수 있습니다. 그리고 그 다음 루프에서 blah의 값을 참조하기 위해 0xffffd68c의 주소를 확인하는데, 방금 전 루프에서 blah 변수의 주소를 변경하였으므로, 엉뚱한 주소를 참조하게됩니다. 그 결과가 두 번째 네모 박스입니다.

그렇다면 어떤식으로 이러한 문제를 해결해야할까요?

저는 overflow시킬 때 blah 변수의 위치에 해당하는 부분에 blah 변수의 주소 값을 넣어주었습니다. 무슨 말이냐면, blah 변수의 값을 그대로 bok 변수에 저장하기 때문에 blah[21]~blah[24]의 위치에 blah의 주소 값을 그대로 때려박아주는 방법을 선택했습니다.

그럼 방법은 이후에 다시 설명하도록 하고, 빨간 네모 박스 쳐 놓은 부분을 확인 해보겠습니다. 




우리가 overflow를 시키고 있는 이유가 바로 ret 값을 덮어 씌우기 위함이었는데요, 그 ret 값의 위치를 알아야 페이로드를 작성 할 수 있겠죠? 첫 번째 쳐 놓은 박스 부분이 ret로 의심되는 부분입니다. 현재 스택의 구조가 (bok[20], &blah, argc, argv, sfp, ret)라고 생각 할 수 있겠습니다. 그럼 해당하는 값이 ret가 맞는지 확인 하기 위해 메인함수를 disassemble 해 보았는데, func 함수를 호출 한 직후의 위치가 기록 되어 있는 것을 확인 할 수 있습니다. 그러므로 해당 주소는 ret가 맞다고 생각하면 될 것 같습니다. 그리고 두 번째 친 네모 박스는 중간에 argv가 맞는 지 확인하기 위해 가져다 놓았습니다. argv[1]의 자리를 확인 해 보니 잘 안착 해 있네요.




그럼 본격적으로 페이로드를 작성해 보도록 하겠습니다. 페이로드 양식은 아래와 같습니다.


- payload : ./narnia8 "(Dummy 20byte) + (&blah) + (Dummy 12byte) + (system got) + (exit got) + (&argv[2])" "/bin/sh"


위 그림에선 AAAA가 Dummy Code, BBBB가 &blah, CCCC가 system got, 마지막 AAAA가 exit의 got(Dummy가 와도 무방함), DDDD가 &argv[2]입니다.

해당 페이로드를 그대로 때려박아주고 argv[1]과 argv[2]의 주소를 확인 해 보니 각각 0xffffd885, 0xffffd8b6이었습니다. 그리고 덤으로 system 함수와 exit 함수의 주소도 확인하였습니다.


- &argv[1] : 0xffffd885

- &argv[2] : 0xffffd8b6

- system got : 0xf7e63cd0

- exit got : 0xf7e56ec0




우리가 구한 주소들을 페이로드 양식에 때려박아주면 위 그림의 빨간 네모박스와 같은 형태가 나옵니다. 그대로 실행 해 주면 gdb에서 쉘이 떨어지는 것을 확인 할 수 있습니다.

하지만 gdb 환경과 bash의 환경에서는 argv[0]의 값이 다르기 때문에 argv[1]과 argv[2]의 주소가 꼬여버려 페이로드를 그대로 넣어주면 쉘이 떨어지지 않습니다. 따라서 argv[0]의 값을 맞춰주기 위해 실행 할 때 '/'를 추가하여 이름을 늘려주면서 실행시켰습니다.




/를 총 8번 사용하여 실행 시켰을 때 쉘이 떨어졌으며, narnia9의 패스워드를 알아내면서 OverTheWire Narnia 난이도는 끝을 맺도록 하겠습니다.


OWASP WebGoat

Buffer Overflows - Off-by-One Overflows



Buffer Overflows 카테고리로 넘어왔습니다. 웹앱 내에서의 BOF를 다룰 것 같네요. 지문을 보도록 합시다.



지문을 확인 해 보니 VIP 손님이 묵는 곳이 어떤 방인 지 찾아내라고 합니다.

별다른 힌트가 없으니 소스 코드 상에 뭔가 있을 것 같습니다. [Java Source]를 눌러 소스를 확인 해 보도록 하겠습니다.




쭉 훑어 보니 param3의 길이가 4096을 넘어 갈 때 VIP의 이름과 방 번호를 뱉어내는 분기문이 있습니다.

위 쪽의 체크 박스를 확인 해 보면 Param1, Param2, Param3는 각각 First Name, Last Name, Room Number를 나타내는 것을 알 수 있습니다.

여기서 소스 상에 나타난 First Name과 Last Name, Room Number를 이용하여 바로 세션을 패스 할 수도 있겠지만, 정석으로 풀어내기 위해 Room Number에 4096을 넘어가는 길이의 값을 넣어보도록 합시다.




python을 이용해 4097개의 문자열을 만들었습니다.



자, 그럼 이 값을 복사 해 둡시다.



위에서 복사한 값을 Room Number에 기입 해 주고 Submit을 해줍니다.



이 곳은 별 다른 의미가 없으니 바로 Accept 해줍시다.



의도 한 대로 문구가 나타났습니다. 세션을 완료하기 위해 Restart 후 첫 번째 VIP의 이름과 방 번호를 입력 해 봅시다.



Johnathan/Ravern/4321을 기입해 준 후 Submit을 눌러주면




짠! 끝났습니다.








OWASP WebGoat 

Authentication Flaws - Multi Level Login 2



Multi Level Login1에 이어 Login2입니다. 바로 지문 확인 해 보도록 하겠습니다.



Joe의 계정과 패스워드, 그리고 TAN 코드를 주고 Jane으로 로그인 하라고 합니다.





일단 하라는 대로 Joe/banana로 로그인 해 봅시다.




요구하는 1번의 TAN코드를 기입 해 주고, Login1과 동일하게 이 시점에서 Burp Suite로 패킷을 잡아줍시다.



GET으로 함께 보내는 인자 중 hidden_user라는 야리꾸리한 놈이 있습니다.



이 야리꾸리한 놈을 Jane으로 바꾼 후 진행 해 봅시다.




짠.


OWASP WebGoat

Authentication Flaws - Multi Level Login 1



Authentication Flaws의 세 번째 세션인 Multi Level Login 1입니다. 바로 지문을 보도록 하죠.




스테이지1은Jane/tarzan 으로 로그인을 하라고 합니다.




TAN 코드에서 2번째 숫자를 요구하는데, 그대로 입력 해줍니다.



정상적으로 로그인 되면서 스테이지2로 넘어 온 것을 확인 할 수 있습니다.

스테이지2는 Jane에게 보낸 피싱메일로부터 패스워드와 TAN 코드의 첫 번째 숫자를 알아냈다고 합니다.

어떻게든 이를 이용하여 로그인을 하라고 하네요. Burp Suite를 이용하여 Request가 어떻게 날아가는지 확인해보죠.




Jane/tarzan으로 로그인 했을 때 요구하는 TAN 코드는 3번째 숫자지만, 우리는 1번째 숫자만 알고 있으므로 1번에 해당하는 15648을 입력하여 Submit을 눌러줍니다.



Request를 확인 해 보면, GET 요청으로 hidden_tan과 tan이 인자로 함께 날아가는 것을 확인 할 수 있습니다. 여기서 유추 할 수 있는 것은 hidden_tan이 로그인 시 요구하는 TAN코드의 번호라는 것입니다.



우리가 알고 있는 TAN 코드는 1번이므로, hidden_tan의 값을 1로 바꾼 후 Request를 날려봅시다.




정상적으로 Jane 계정으로 로그인이 되면서 클리어 했습니다.


OWASP WebGoat 

Authentication Flaws - Forgot Password



Authentication Flaws 카테고리의 두 번째 세션입니다. Forgot Password라는 주제로, 패스워드를 잊어버렸을 경우의 상황을 가정하고 풀이하는 듯 합니다. 문제를 보도록 하겠습니다.




지문을 확인 해 보면 웹앱에서 패스워드를 잊어버렸을 경우 질의문답이 너무 간단한 메커니즘으로 구성되어 있어 취약하다는 내용입니다.

따라서 admin이라는 계정을 가지고 있지 않지만 패스워드를 획득 해 보라고 합니다.


admin을 시도 해 보기 이전에 지문에서 확인 했던 webgoat 계정부터 패스워드를 알아 내 보도록 하겠습니다.







webgoat라는 Username에 해당하는 질의의 답은 red였습니다. red를 입력하니 바로 webgoat라는 패스워드를 뱉어주네요. 그럼 이제 Username에 admin을 넣어 시도 해 보도록 하겠습니다.




Username에 admin을 넣어주고,



질의의 답으로 red를 입력하여 Submit 한 결과 admin에 적합하지 않은 응답이라고 뱉어냅니다. 그렇다면 다른 힌트가 없으니 게싱을 통해 색상을 적어주다 보면 green이 admin 계정의 질의에 대한 답이라는 것을 알 수 있습니다.




OWASP WebGoat

Authentication Flaws - Password Strength


과제로 시작하는 WebGoat 첫 포스팅입니다.

금주 WebGoat 과제는 Authentication Flaws 카테고리입니다. 인증 결함을 찾아내는 부분인 것 같습니다. 총 4개의 세션으로 이루어져 있으며, 이 중 첫 번째 세션인 Password Strength를 풀이 해 보도록 하겠습니다.




지문을 확인 해 보니, 우리가 사용하는 웹앱에서 계정을 보호하기 위해 https://howsecureismypassword.net에서 패스워드 검증을 시도 해 보라고 합니다. 총 6개의 패스워드가 주어져 있으며, 이를 해당 웹페이지에 입력하면 크랙 될 때 까지의 시간을 출력해주는 형식입니다. 바로 넣어보도록 합시다.


위 웹페이지가 패스워드 검증 사이트인데, 두 번째에 해당하는 패스워드인 abzfezd를 입력 했을 때의 출력 결과입니다. 이런 식으로 모두 입력 해 보았을 때의 결과는 아래와 같습니다.



23456 - 0 seconds (Instantly)

abzfezd - 0.2 seconds (2 Hundred Milliseconds)

a9z1ezd - 2 seconds

aB8fEzDq - 2 hours

z8!E?7D$ - 2 days

My1stPassword!:Redd - 36 quintillion years (3600경 년)


하지만 위 결과를 입력했을 때 정답이 출력되지 않아, Solution 탭을 확인하여 정답을 보니 전혀 다른 결과가 있었습니다.

아마 PC마다 다른 결과가 나타나거나 검증 웹페이지 알고리즘이 변경 되어 다르게 표기 된 듯 합니다.




위의 결과를 입력 해 보면 해당 세션을 패스 할 수 있습니다.









OverTheWire_Wargame Narnia

[ level7 -> level8 ]





level7의 소스입니다. 이전 레벨에서 볼 수 있었던 snprintf 함수의 취약성을 이용한 FSB와 비슷한 듯 합니다.

차이점으로는 main 함수에서 직접 처리하는 것이 아닌 vuln 함수에서 처리한다는 점 빼곤 없어 보입니다. 아, 또 다른 점으로는 buffer 변수의 결과 값을 출력 해 주지 않는다는 점이 있겠습니다. 자, 그럼 실행하면서 어떤식으로 풀어야 할 지 찾아보도록 하죠.



prtf() = 0x80486e0 (0xffffd64c) 부분을 확인 해 보면 괄호 안에 있는 부분은 ptrf 포인터 변수의 주소이고, 왼쪽의 값은 goodfunction 함수의 주소입니다. 이 값을 hackedfunction의 주소인 0x8048706으로 변경하여 shell을 띄우는 게 이번 레벨의 목표인 것 같습니다.

하지만 이전 레벨과 달리 buffer의 변수를 printf로 출력 해 주지 않기 때문에 얼마나 %x 서식문자를 입력 해 주어야 buffer 변수에 접근 할 수 있는지 알 수가 없습니다. 따라서 gdb를 이용해 스택 값을 확인하여 0xffffd64c의 값의 변화를 직접 확인하는 쪽으로 풀이하였습니다. 노가다죠.



vuln 함수에서 snprintf를 한 직후에 breakpoint를 걸어주고 실행하였습니다. 여기서 FSB를 위해 측정한 거리는 다음과 같습니다.


34542 = 0x8706(34566) - 16 - 8

33046 = 0x10804(67588) - 34542


위와 같이 거리를 구한 후 대입 해 주었으나, gdb 상에서 실행하였기에 파일명이 절대경로로 들어가 argv[0]이 변해서 ptrf의 주소 값이 바뀌었습니다. 그래서 그대로 대입을 해 주었으나, ptrf의 값이 바뀌지 않은 것을 보니 %8x를 한 번 사용했을 때는 buffer 변수에 접근을 못하는 듯 합니다. %8x와 거리 값들을 바꿔주변서 계속 시도하다 보면 아래와 같이 ptrf 포인터의 값이 변하는 부분을 찾을 수 있습니다.



%8x를 총 5번 사용했을 때 buffer 변수에 접근 할 수 있다는 것을 확인 할 수 있는데, 이상한 게 한 가지 있습니다.

예상대로라면 buffer 변수에 접근했을 때 ptrf 포인터의 값은 0x08048706이 되어야 하는데, 0x8706은 정상적으로 overwrite 된 반면 0x0804는 0x083c로 들어가 있는 것을 확인 할 수 있었습니다. 원인은 공부가 부족하여 찾지는 못했습니다. 계산법이 틀린건지, 놓친 부분이 있는 것인지는 잘 모르겠습니다. 혹시 아시는 분이 있다면 댓글로 지적 부탁드립니다. :D

일단 답을 찾아내기 위해... 3c에서 04까지의 거리인 0x38(56)을 빼주고 실행해봅시다.



어떻게든 답은 찾아냈습니다. :D

+ Recent posts