안경잡이개발자

728x90
반응형

  flee는 '달아나다'라는 뜻을 가지고 있습니다. 말 그대로 버튼이 달아나는 상황에서 버튼을 클릭해야 되는 문제라고 이해할 수 있습니다. 따라서 이 문제 유형 또한 웹 해킹 문제 유형입니다.

 

 

  문제 풀이를 시작하면 다음과 같이 [click me!]라는 버튼이 등장합니다. 이 버튼을 클릭하면 정답 처리를 받을 수 있다는 의미인 것 같습니다. 다만 마우스를 움직이면 이 버튼이 마우스를 피하여 이리저리 움직인다는 특징이 있습니다.

 

 

  따라서 개발자 도구(F12)를 실행하여 다음과 같이 [요소 선택] 버튼을 누릅니다. 그러면 화면이 정지 상태가 되며, 이 때 버튼을 클릭할 수 있습니다.

 


  그러면 다음과 같이 플래그(Flag) 값이 등장합니다. 또한 이 문제는 개발자 도구에서 [Elements] 탭으로 이동하여 소스코드 상에서 버튼 클릭 함수를 확인하고, 그것을 콘솔 창에 입력하는 방식으로도 문제를 풀 수도 있답니다. 

 

728x90
반응형

Comment +2

728x90
반응형

  already got 문제는 가장 쉬운 문제 유형으로, 웹 해킹 문제입니다.

 

 

  웹 페이지에 접속하면 다음과 같이 이미 Key 값을 얻었다는 메시지가 나옵니다. 그렇다면 서버로부터 이미 Key를 전달 받았다는 의미로 이해할 수 있습니다.

 

 

  따라서 개발자 도구(F12)를 실행한 뒤에 새로고침을 해보면, 다음과 같이 [Network] 탭에서 서버로부터 전달 받은 응답헤더(Response Header)에 [FLAG] 값이 포함되어 있는 것을 확인할 수 있습니다.

 

 

  결과적으로 다음과 같이 제출하여 정답 처리를 받을 수 있었습니다.

 

728x90
반응형

Comment +0

728x90
반응형

wargame.kr - Crypto Crackme Basic 문제풀이(Write Up)

문제 분류: 리버싱(Reversing)

 

  이번 시간에 풀어 볼 문제 또한 wargame.kr에서 제공하는 간단한 리버싱(Reversing) 문제입니다. 문제 이름은 Crypto Crackme Basic이며 간단한 암호화 라이브러리 코드에 대한 분석을 물어보는 문제입니다.

 

  ▶ 문제풀이 사이트 주소: http://wargame.kr

 

  바로 문제에서 제공해주는 프로그램을 다운로드 받아서 실행해봅시다.

 

  다만 본 프로그램은 C# .NET Framework를 이용해 개발이 된 것으로 보입니다. 따라서 프로그램을 실행하는 과정에서도 .NET Framework가 설치되어 있는지 물어봅니다. 저는 설치가 안 되어 있어서, .NET Framework 3.5를 설치하는 과정이 필요했습니다.

 

 

    설치 이후에 프로그램을 실행할 수 있는데, 이름과 비밀번호를 정확히 입력해야 Flag를 얻을 수 있는 문제 유형인 듯 합니다.

 

 

  가장 먼저 IDA Pro를 이용하여 프로그램이 어떻게 동작하는지 확인하도록 하겠습니다. 나아가 적절한 Name과 Password 값을 찾아야 정답 처리를 받을 수 있을 것입니다.

 

 

  다만 기본적인 IDA Pro를 이용해서는 C# 프로그램을 쉽게 분석할 수 없다는 것을 깨달았습니다. 다시 말해 소스코드를 제대로 분석하기 어려운 상태입니다. 그러므로 동적 분석을 하거나 C# 전용 디컴파일러가 필요할 것 같습니다. 저는 대표적인 C# 디컴파일러인 dotPeek을 이용하도록 하겠습니다.

 

  dotPeek 다운로드: https://www.jetbrains.com/decompiler/

 

dotPeek: Free .NET Decompiler & Assembly Browser by JetBrains

dotPeek is a free tool based on ReSharper. It can reliably decompile any .NET assembly into C# or IL code.

www.jetbrains.com

 

  공식 홈페이지에 접속한 이후에는 기본 설정 그대로 설치를 진행할 수 있습니다.

 

 

  바로 [DOWNLOAD] 버튼을 눌러서 기본 설정 그대로 설치를 진행합니다.

 

 

 

  다른 프로그램은 받지 않고, dotPeek만 선택하여 INSTALL하였습니다.

 

 

  설치 이후에는 dotPeek을 실행합니다.

 

 

  실행 이후에는 프로그램을 Drag & Drop하여 놓습니다. 그러면 자동으로 해당 프로그램을 정적 분석할 수 있도록 도와주는 것을 알 수 있습니다.

 

 

  이제 전체 소스코드를 거의 원래 개발 환경에서의 상태 그대로 확인이 가능합니다. 다시 말해 소스코드 분석을 위해 직접 C# 개발환경을 구축해서 동적으로 분석하는 방법도 있지만, 이렇게 간단히 C# 디컴파일러 도구를 이용하는 것 만으로도 리버싱이 가능한 것 입니다. 이제 하나씩 함수를 분석하겠습니다.

 

 

  먼저 mPadding 함수입니다. mPadding(string s) 함수는 다음과 같이 동작합니다.

- s의 길이가 8자인 경우, 문자열 그대로 반환 
- s의 길이가 8자를 초과하는 경우, 문자열의 뒤쪽 8자만 반환 
- s의 길이가 8자 미만인 경우, 부족한 길이만큼 문자열 뒤에 '*'를 추가하여 반환

 


  getps(string name) 함수는 다음과 같이 동작합니다.

- 특정한 웹 서버로 파라미터 값을 전송하고, 웹 서버의 응답 값을 가져 옴

 


  myEncrypt(string password, string name) 함수는 다음과 같이 동작합니다.

- 입력 받은 비밀번호를 "mPadding(name)"으로 DES 암호화 한 결과를 Base64로 인코딩하여 반환 

  이제 이러한 정보를 활용하여 문제를 풀 수 있습니다.

 


  일단 서버로 보내지는 값은 항상 같습니다. 왜냐하면 BluSH4G이라는 문자열은 8자 미만이므로 항상 뒤에 *이 붙기 때문입니다. 따라서 소스코드를 분석한 결과, 최종적으로 호출되는 URL 주소는 다음과 같습니다.

 

http://wargame.kr:8084/prob/28/ps.php?n=BluSH4G*

 

 

  접속 결과 반환되는 문자열은 다음과 같다는 것을 알 수 있습니다.


JbY4qKhzRgPY1rtQayPU+RtQJ8+ndUFC1wz8h0eBOEsRs00MHv9Czv2luONBu34b 

  이제 이 값을 DES 복호화 하면 요구하는 비밀번호가 무엇인지 알아낼 수 있는 것입니다. Base64 형태를 가지고 있는 DES 암호화 구문을 특정한 키로 DES 복호화 해주는 사이트가 있으니, 이를 이용하면 될 것 같습니다.

https://www.tools4noobs.com/online_tools/decrypt/ 

 

  다음과 같이 DES 암호화의 Key 값을 함께 넣어서 복호화를 진행합니다.

 

 

  이렇게 구한 Flag 값을 기입하면 정답 처리를 받을 수 있습니다.

 

728x90
반응형

Comment +0

728x90
반응형

wargame.kr - DLL with notepad 문제풀이(Write Up)

문제 분류: 리버싱(Reversing)

 

  이번 시간에 풀어 볼 문제 또한 리버싱(Reversing) 문제입니다. 문제 이름은 DLL with notepad로 간단한 DLL 모듈을 분석할 수 있는 지에 대해서 물어보는 문제입니다.

 

  ▶ 문제풀이 사이트 주소: http://wargame.kr

 

  사이트에 접속하면 두 개의 파일이 들어 있는 압축 파일을 다운로드 받게 됩니다. 압축을 풀어 주고 다음과 같은 두 파일을 확인합니다.

 

 

  실제로 노트패드 애플리케이션을 실행해 보면, 메모장 프로그램이 실행되는 것을 알 수 있습니다.

 

 

  아무래도 이러한 메모장 프로그램이 실행됨과 동시에 blueh4g13.dll 모듈이 함께 실행되는 것으로 판단할 수 있습니다. 문제부터 DLL을 사용한다고 명시하고 있기 때문입니다. 따라서 가장 먼저 IDA Pro 32 bit을 실행하여 blueh4g13.dll 파일을 열어 보도록 하겠습니다.

 

  그랬더니 DLL 모듈의 메인 함수 부분이 눈에 보입니다.

 

 

  막막할 때는 해당 모듈에 어떤 문자열이 포함되어 있는지 확인하는 것이 가장 좋습니다. 따라서 Shift + F12로 [String View]를 실행합니다.

 

 

  확인 결과 'oh! handsome guy!'라는 수상한 문자열을 확인할 수 있었습니다. 따라서 더블 클릭하여 문자열이 참조되는 함수를 찾아가 보았습니다. start+49 함수에서 해당 문자열을 참조하는 것을 알 수 있습니다. 따라서 이를 따라가서 F5를 눌러 Hex-Ray를 이용합시다.

 

 

  그랬더니 다음과 같이 DLL 모듈이 notepad.exe와 연동되는 코드를 확인할 수 있습니다. wcsicmp는 문자열 비교 함수로서 두 문자열이 같을 때 0이 반환됩니다. 따라서 전체 소스코드를 살펴 보자면 정확히 "notepad.exe"라는 프로그램과 연동되는 것입니다. 또한 StartAddress라는 쓰레드 함수가 실행되는 것을 확인할 수 있습니다. 따라서 StartAddress 함수를 확인합시다.

 

 

  함수를 방문한 결과, 사용자가 입력한 문자가 dword_10003388과 동일할 때 정답 처리를 받을 수 있도록 소스코드가 작성되어 있는 것을 확인할 수 있습니다. 그렇다면 dword_10003388에 Password가 담겨 있을 가능성이 높습니다.

 

 

  따라서 dword_10003388을 더블 클릭하여, 해당 문자열이 어떤 함수에서 불려지는지 확인할 수 있습니다.

 

 

  아래 사진을 보시면 dword_10003388이라는 배열에 한 글자씩 v2의 값을 넣는 것을 알 수 있습니다. v2는 어떤 값인지 명확히 판단이 어렵지만, 시간 변수를 적절히 조절해서 임의의 값을 생성한 뒤에 이를 v2에 넣는 것임을 알 수 있습니다. 아무래도 정적 분석은 쉽지 않을 것 같습니다. 동적 분석이 가능한 상황이므로 동적 분석을 진행하도록 하겠습니다.

 

 

  이쯤에서 한 가지 의문점을 가져야 합니다. dword_10003388의 값이 언제 Update 되는 지 고려할 수 있습니다. 실제로 dword_10003388가 언제 참조되는지 확인해보면, 세 가지 명령어에서 참조되는 것을 알 수 있습니다. 다음과 같습니다.

 

 

  첫 번째 명령어: dword_10003388을 0으로 초기화

  두 번째 명령어: dword_10003388을 임의의 문자열로 만들기

  세 번째 명령어: dword_10003388이 사용자가 입력한 텍스트와 동일한 지 비교 

 

  결과적으로 dword_10003388은 임의의 문자열이 대입 된 이후에는 값이 다시 변경되지 않습니다. 정리하자면 notepad.exe가 실행되어 dll 모듈이 적재된 순간에 dword_10003388에 저장되어 있는 값을 출력하면, 그것이 바로 Password일 것이라고 유추할 수 있습니다.

 

  그렇다면, 이러한 dword_10003388에 담겨 있는 값은 어떻게 꺼내 올 수 있을까요? 앞서 언급했듯이 코드가 복잡할 때는 정적 분석보다는 동적 분석을 추천합니다. 이럴 때는 OllyDbg를 이용하면 쉽게 문제를 풀 수 있습니다.

 

※ OllyDbg를 이용한 방법 ※

 

  OllyDbg로 한 번 노트 패드 프로그램을 열어 보겠습니다.

 

 

  ALT + E를 눌러 [Executable modules] 탭을 열면, 해당 프로그램과 연동된 DLL 모듈을 확인할 수 있습니다. blueh4g13.dll이 보이네요. 더블 클릭합니다.

 

 

  blueh4g13.dll의 창을 불러 온 다음에 실행 버튼을 눌러서 노트 패드가 켜지는 것을 확인합니다.

 

 

  이제 실행이 되었으니, DLL 모듈이 성공적으로 초기화 되었겠죠? 이제 우클릭 하여 [Search for] - [All referenced text strings]를 눌러서 해당 DLL에 존재하는 문자열 정보를 확인합니다.

 

 

  그랬더니 다음과 같이 문자열에 Password 값으로 보이는 문자열이 발견되었습니다.

 

 

  다음과 같이 Password 값을 이용하여 [auth] 버튼을 누르면 Flag 값을 얻을 수 있습니다.

 

 

※ Process Explorer를 이용하는 방법 ※

 

  또한 사실 런타임 환경에서 dll 모듈에 저장되어 있는 변수의 값을 찾는 방법으로 프로세스 익스플로러(Process Explorer)를 이용하는 방법이 있습니다. DLL 모듈이 초기화 된 이후에 String 값은 Memory 영역에 올라가기 때문에 이를 검토할 수 있습니다.

 

  Process Explorer에서 현재 실행 중인 Notepad 프로그램을 선택하면, 아래 쪽에 함께 실행 중인 서브 프로세스를 확인할 수 있습니다.

 

 

  [Memory] 탭으로 이동하여 확인해도, 아까와 마찬가지로 Password 값이 보이는 것을 알 수 있습니다. 하지만 일반적인 경우 위에서 다룬 OllyDbg를 이용한 동적 분석 방식이 효과적으로 사용됩니다.

 

 

728x90
반응형

Comment +0

728x90
반응형

wargame.kr - keypad CrackMe 문제풀이(Write Up)

문제 분류: 리버싱(Reversing)

 

  이번 시간에 풀게 될 문제는 간단한 리버싱(Reversing) 문제입니다.

 

  ▶ 문제풀이 사이트 주소: http://wargame.kr

 

  문제에 접속하면, 패스워드를 찾아보라는 안내 메시지와 동시에 하나의 실행 파일이 존재합니다.

 

 

  프로그램을 실행하여 간단히 키패드에 아무 내용이나 입력한 뒤에 [AUTH] 버튼을 눌러봅시다.

 

 

  아무래도 정확한 패스워드 값을 넣어야 정답 처리가 되는 것 같습니다. 따라서 IDA Pro를 실행하여 해당 프로그램을 대상으로 정적 분석을 진행하겠습니다. 가장 먼저 [Shift + F4]를 눌러서 [Names View]에 'password'를 검색합니다. 그러면 위에서 확인했던 wrong password라는 값을 가지고 있는 변수를 찾아낼 수 있습니다.

 

 

  이제 이 변수는 어디에서 사용되고 있을까요? 검색 결과, sub_401740라는 함수에서 사용되고 있는 것을 확인할 수 있습니다.

 


   따라서 해당 변수를 참조하고 있는 함수인 sub_401740을 더블 클릭하여 확인해보겠습니다. 그랬더니 무언가 인증을 수행하는 느낌의 어셈블리어 코드가 나오는 것을 알 수 있습니다.

 

 

  이후에 F5를 눌러서 Hex-Ray를 사용해 봅시다. 그랬더니 다음과 같이 상당히 유추하기 쉬운 소스코드가 등장하는 것을 알 수 있습니다.

 

 

  소스코드를 있는 그대로 확인해보겠습니다. 아무래도 사용자가 입력한 값은 v3 변수에 담기는 것 같습니다. 이제 그 변수에서 숫자만 추출하여 정수형으로 만들어 v5에 담습니다. 그리고 v6에는 현재 달 정보가 담기는 것을 알 수 있습니다.

 

  따라서 -201527 * 7월 + v5 == 195934910입니다.

 

  그러므로 정답은 "현재 달(포스팅 시점은 7월) * 201527 + 195934910"입니다. 저는 계산기를 이용해 정답이 197345599인 것을 알아냈습니다. 아마 이 포스팅을 접하고 계신 분들의 현재 날짜에 따라서 정답이 달라질 것으로 예상할 수 있습니다.

 

 

  실제로 해당 값을 입력하여 [AUTH] 버튼을 누르면 성공적으로 Flag 값이 도출됩니다. 실제 Flag 값은 직접 리버싱을 통해 확인해보세요!

 

728x90
반응형

Comment +0

728x90
반응형

wargame.kr - EASY_CrackMe 문제풀이(Write Up)

문제 분류: 리버싱(Reversing)

 

  이번 시간에 풀어 볼 문제는 대표적인 워게임(Wargame) 사이트인 wargame.kr에서 제공하는 간단한 리버싱(Reversing) 문제입니다. 문제 이름은 EASY_CrackMe입니다.

 

  ▶ 문제풀이 사이트 주소: http://wargame.kr

 

Wargame.kr - 2.1

https://www.facebook.com/wargame.kr

wargame.kr

 

  바로 문제풀이 사이트에서 [EASY_CrackMe] 문제를 선택해서 풀어보겠습니다.

 

 

  [Start] 버튼을 눌러서 프로그램 파일을 확인합시다.

 

 

  파일을 다운로드 하여 실행을 해보겠습니다.

 

 

  실행을 하면, 단순히 무언가를 입력하여 제출하는 창이 나옵니다.

 

 

  일단 아무 텍스트나 입력한뒤에 [check] 버튼을 누르면, 다음과 같이 틀렸다는 내용이 등장합니다. 확인 결과 문자열은 nono.. 인 것으로 판단됩니다.

 

 

  이제 IDA Pro 32bit을 실행하여 해당 CrackMe1st.exe 파일을 분석해보도록 하겠습니다.

 

 

  처음 IDA Pro로 프로그램을 불러오면, 위와 같이 프로그램의 크기가 작지 않기 때문에 로딩에 있어서 약간의 시간이 소요됩니다. 일단 가장 먼저 [Quick View(Ctrl + F1)]을 실행하여 [Names View]를 확인합니다.

 

 

  이후에 찾고자 하는 문자열인 "nono"를 검색하여 더블 클릭 합니다.

 

 

  그러면 다음과 같이 텍스트 부분이 나옵니다.

 

 

  이제 마우스 우클릭을 한 뒤에 [List cross references to...] 버튼을 눌러서 이를 참조하는 코드를 확인합니다.

 

 

  그러면 다음과 같이 함수 내용이 나옵니다.

 

 

  이제 함수가 등장했으므로, [F5]를 눌러서 Hex-Ray를 동작시킵니다.

 


  그러면 다음과 같이 Hex-Ray로 내용을 살펴 볼 수 있습니다. 보시면 "G00d!"라고 출력되는 부분이, 바로 정답 처리를 부분이라고 판단할 수 있습니다. 또한 서버와 통신하는 부분이 들어가 있습니다. 즉, 정답을 서버로 제출하게 되고 서버가 실질적인 Flag 값을 주는 것으로 파악할 수 있습니다.

 

 

  소스코드를 확인해 보시면 다음과 같이 분석할 수 있습니다.

 

  - v1[30]: 사용자가 입력한 문자열

  - v4: 문자열의 길이

  - wcsstr(A, B): A 문자열에 B 문자열이 포함되어 있는지 확인합니다.

  - wtoi(A): A 문자열을 정수 형태로 변경합니다.

 

  따라서 소스코드의 내용을 다음과 같이 정리할 수 있습니다.

 

  문자열의 길이가 정확히 13글자이면서, 문자열에 _my_b와 birth가 포함됩니다. 그와 동시에 문자열이 1114로 시작해야 합니다. 왜냐하면 wtoi() 함수는 앞에서부터 출발하여 숫자가 아닌 문자를 만났을 때, 거기서부터는 수로 변경하지 않기 때문입니다.

 

  이러한 조건을 만족하는 문자열은 1114_my_birth입니다.

 

 

  따라서 해당 정답을 제출하면, Flag 값을 받을 수 있습니다.

 

 

  혹은 서버의 API 주소에 바로 정답 데이터를 보내도 Flag를 얻을 수 있었습니다.

728x90
반응형

Comment +1

  • Karatus 2020.04.21 19:30

    안녕하세요. 문제풀이 잘 봤습니다.
    하지만 하나 궁금한 점이 저 같은 경우에는 함수명이 제대로 표시되지 않는 문제점이 있었습니다.
    우여곡절 끝에 겨우 풀었지만 후에 이 풀이를 참고해 정적, 동적 툴을 이용한 모든 분석에서 여전히 해당 문제가 남아있었습니다.
    그래서 해당 함수가 정의되어 있는 dll 파일을 찾아 넣어주기도 했지만 결과는 같았습니다.
    혹시 어떻게 해야 해결할 수 있는지 아신다면 알려줄 수 있으신지 여쭤보고 싶습니다.
    감사합니다.

728x90
반응형

pwnable.kr - flag 문제풀이(Write Up)

문제 분류: 리버싱(Reversing)

 

  이번 시간에 풀어 볼 문제는 pwnable.kr의 flag입니다. 이 문제는 가장 기본적인 리버싱(Reversing) 도구를 활용하는 문제입니다.

 

  ▶ 문제풀이 사이트 주소: http://pwnable.kr/play.php

 

 

  위 URL에 명시되어 있는 곳으로 접속하면, 하나의 파일이 다운로드 됩니다. 파일은 flag이며, 이 프로그램을 분석하면 되는 문제입니다.

 

 

  분석을 위해 IDA Pro 32bit 버전을 실행합니다.

 

 

  다음과 같이 프로그램을 열 수 있습니다.

 

 

  다만, 프로그램이 32bit 프로그램이 아니라 64bit라고 하네요.

 

 

  따라서 IDA Pro 64bit 버전을 실행해서 다시 엽니다.

 

 

  이제 분석된 소스코드를 확인합니다. [Graph View]로 보면 정상적으로 코드를 분석하기 어렵습니다.

 

 

  따라서 스페이스 바(Space Bar)를 눌러서 [Text View]로 접속합니다.

 

 

  다만, 어셈블리어가 정상적으로 출력되지 않는 것을 알 수 있습니다. 이럴 때는 일단 프로그램에 포함되어 있는 Strings에 대한 정보를 확인하면 됩니다. 따라서 [Quick View(Ctrl + 1)]을 확인하여, [Strings(Shift + F12)]를 열면 됩니다.

 

 

  그러면 다음과 같이 UPX에 대한 내용을 확인할 수 있습니다. 바로 이 UPX 때문에 우리의 프로그램이 정상적으로 분석되지 않고 있다는 것으로 이해할 수 있습니다.

 

 

  이는 프로그램이 UPX(Ultimate Packer for eXecutable)를 이용하여 패킹되어 있다는 것을 암시합니다. UPX는 다양한 파일 포맷을 지원하는 실행 파일 압축 프로그램입니다. 실행 파일 압축을 하게 되면, 파일의 크기를 줄일 수 있으며 기본적인 리버싱을 방어할 수 있다는 장점이 있습니다.

 

  또한 이와 같이 패킹(Packing) 처리가 된 프로그램을 다시 언패킹(Unpacking)해서 원래의 소스코드를 제대로 확인할 수 있습니다. 언패킹 도구는 다음의 경로에서 확인할 수 있습니다.

 

https://github.com/upx/upx/releases/tag/v3.94

 

upx/upx

UPX - the Ultimate Packer for eXecutables. Contribute to upx/upx development by creating an account on GitHub.

github.com

 

  윈도우 사용자의 경우 upx394w.zip을 다운로드 받아서 압축을 풀어줄 수 있습니다.

 

 

  이제 압축을 푼 폴더에 flag 프로그램을 위치시킵니다.

 

 

  이후에 명령 프롬프트(CMD) 상에서 해당 폴더로 이동하여 upx -d [파일 이름] 명령을 이용하여 특정한 파일을 언패킹 할 수 있습니다.

 

 

  이후에 다시 언패킹 된 flag 파일을 IDA Pro 64bit로 열면 다음과 같이 깨지지 않고 코드가 나옵니다.

 

 

  이제 [Text View]를 확인하면 Flag 값을 확인할 수 있습니다. 이것은 전체 플래그 값이 아니며, 플래그 값을 얻기 위해서는 직접 분석해보시는 것을 추천드립니다.

 

 

728x90
반응형

Comment +0

728x90
반응형

pwnable.kr - lotto 문제풀이(Write Up)

문제 분류: 공격(Attack)


  이번 시간에 풀어 볼 문제는 pwnable.kr의 lotto입니다. 기존에 기초 프로그래밍 위주로 학습하고 보안 분야로 넘어오신 분들에게 적합한 문제라고 생각되어 이 문제 또한 앞 부분에서 다루게 되었습니다. 더불어 난이도 또한 낮은 기초 문제라서 기초 C언어 지식으로도 문제를 해결하실 수 있답니다.


  ▶ 문제풀이 사이트 주소: http://pwnable.kr/play.php



  이 문제는 SSH를 이용해 통신해야 하는 문제입니다. SSH는 원격지에 있는 서버에 접속하여 서버를 관리할 수 있도록 해주는 프로토콜입니다. 윈도우 10은 기본적으로 OpenSSH를 내장하고 있어 쉽게 SSH를 사용할 수 있지만 사용이 어려운 경우 Putty와 같은 별도의 소프트웨어가 필요할 수 있습니다. Putty는 https://www.putty.org에 접속하여 설치가 가능합니다.



  저는 위와 같이 윈도우에 내장된 OpenSSH를 이용해 서버에 접속해보았습니다.



  접속 이후에는 ls 명령어를 이용해 위와 같이 현재 루트 디렉토리에 어떠한 파일이 존재하는지 확인할 수 있습니다. lotto 실행 파일과 lotto.c 소스코드 파일이 존재합니다. 일반적으로 이러한 분석 및 공격 분류의 문제는 소스코드를 보면서 취약점을 파악해 실행 파일에 적용하여 Flag 값을 얻을 수 있는 방식입니다. 일단 간단히 ./lotto 명령어로 로또 프로그램을 테스트해보겠습니다.



  6자리의 로또 코드를 입력하라고 하는 군요. 다만, 제가 알기로 로또는 1부터 45까지의 숫자를 넣는 것인데 6자리를 문자열로 넣는 것이 조금 이상하다는 생각이 들었습니다. 일단 아래와 같이 cat lotto.c 명령어를 입력해 lotto.c 소스코드를 분석할 수 있습니다.



  소스코드를 확인한 결과 랜덤한 6개의 숫자가 lotto[] 배열에 저장되고, 숫자는 아스키 코드로 1부터 45까지의 랜덤한 숫자로 추출됩니다. 다만 아래쪽을 보면 이중 For문을 이용해 우리가 입력한 문자열과 배정된 당첨 lotto[] 번호가 일치하는지 확인하는 구문이 나옵니다. 저의 문자열 매칭 알고리즘 강의(https://blog.naver.com/ndb796/221240660061)를 들으신 분이라면 위 알고리즘이 잘못되었다는 것을 쉽게 파악할 수 있을 겁니다. 위 알고리즘은 단 하나의 로또 번호라도 일치한다면, match 변수의 값이 6이 됩니다. 즉, 하나만 번호가 일치해도 당첨이 되어버리는 잘못 작성된 소스코드라고 할 수 있습니다.


  우리는 이렇게 잘못 작성된 알고리즘을 이용하여 로또에 쉽게 당첨되는 공격(Attack) 시도를 할 것입니다.



  먼저 아스키 코드표를 봅시다. 이제 위에서 보이는 1부터 45까지의 아스키 코드 중에서 아무거나 6개를 넣으면 됩니다. 그러면 우연히 1개의 문자라도 로또 당첨 번호와 일치하는 경우 플래그(Flag) 값을 내뱉을 것입니다. 통계적으로 생각했을 때 대략 3~4번만 시도해도 답이 도출될 것이라 판단할 수 있습니다.



  위와 같이 문자열 "######"으로 몇 번 입력을 시도하니 플래그 값이 나왔습니다.

728x90
반응형

Comment +2

728x90
반응형

pwnable.kr - coin1 문제풀이(Write Up)

문제 분류: 코딩(Coding)


  이번 시간에 풀어 볼 문제는 대표적인 CTF 문제풀이 사이트인 pwnable.kr의 기초 문제 중 'coin1' 문제입니다. CTF 공부 카테고리에서 실질적으로 풀어보는 첫 번째 문제가 될 겁니다. pwnable.kr 공식 웹 사이트에서 coin1 문제를 확인하실 수 있습니다.


  ▶ 문제풀이 사이트 주소: http://pwnable.kr/play.php



  문제를 확인해보면 pwnable.kr에 9007번 포트로 접속해보라고 나오는 것을 알 수 있습니다.



  문제를 요약하면 다음과 같습니다.

 

- N은 코인의 개수, C는 시도 횟수

- 서버로 코인의 번호들을 보내면 서버는 해당 코인들의 무게 합을 반환합니다.

- 가짜 코인이 하나 숨어있는데, 가짜 코인의 무게는 9이고 정상 코인의 무게는 10입니다.

 

  따라서 이 문제를 해결하기 위해서는 이분 탐색을 수행하면 됩니다. 예를 들어 전체 코인이 4개가 있고, 각 코인의 무게가 다음과 같다고 해봅시다.

 

코인: 0 1 2 3

무게: 10 10 9 10

 

  이 때 맨 처음에 서버로 “0 1”을 보낼 수 있습니다. 그러면 서버에서는 두 코인의 무게 합인 20을 반환할 겁니다. 우리는 이 20이라는 결과를 통해서 , 01 중에는 가짜 코인이 없구나.’ 라는 것을 알 수 있습니다. 그러면 01은 더 이상 볼 필요 없이 23을 보면 됩니다.

 

  23 중에서 반드시 하나는 가짜 코인이 섞여있다는 것을 알고 있기 때문에 “2”만 보내면 됩니다. 만약에 서버로부터 돌아오는 결과가 9라면 2가 가짜 코인이고, 서버로부터 돌아오는 결과가 10이라면 3이 가짜 코인이 되는 것입니다.

 

  이와 같이 현재 가짜 코인이 섞여있는 코인의 집합 중에서 계속해서 절반(Half)만 확인하는 식으로 매 번 검사할 때마다 검사하는 집합의 크기를 1/2로 줄일 수 있습니다. 이것은 이분 탐색의 대표적인 예라고 할 수 있으며 통상적으로 코인의 개수가 N개일 때 O(logN)의 시간 복잡도를 가집니다.

 

  이제 문제를 푸는 방법을 알았으므로 Java를 이용해 서버 통신 모듈을 개발합니다.

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


private static Socket sock = null;

private static OutputStream out = null;

private static InputStream in = null;

private static BufferedWriter bw = null;

private static BufferedReader br = null;


public static void init() {

try {

sock = new Socket("pwnable.kr", 9007);

out = sock.getOutputStream();

in = sock.getInputStream();

bw = new BufferedWriter(new OutputStreamWriter(out));

br = new BufferedReader(new InputStreamReader(in));

} catch (Exception e) {

e.printStackTrace();

}

}


public static void close() {

try {

bw.close();

br.close();

sock.close();

} catch (IOException e) {

e.printStackTrace();

}

}


public static int findAnswer() {

    try {

    // 서버로부터 메시지를 전달 받습니다.

    String line = null;

        while((line = br.readLine()) != null){

        // 전달 받은 메시지를 출력합니다.

        System.out.println(line);

        // 첫 번째 문자가 'N'인 경우 문제 풀이가 시작된 것입니다.

        if(line.length() != 0 && line.charAt(0) == 'N') break;

        }

        // N과 C를 파싱하여 각각 숫자로 변환합니다.

        int N = Integer.parseInt(line.split("N=")[1].split(" C")[0]);

        int C = Integer.parseInt(line.split(" C=")[1]);

        System.out.println("N의 값은 " + N + ", C의 값은 " + C);

    } catch (Exception e) {

    e.printStackTrace();

    }

return -1;

    }


public static void main(String[] args) {

try {

init();

findAnswer();

close();

} catch (Exception e) {

System.out.println(e);

}

}

}

 

  전체 소스코드는 소켓 통신 관련 객체를 초기화하는 init() 함수, 소켓 통신 관련 객체를 파기하는 close() 함수, 서버와 직접 통신해 답을 찾아내는 findAnswer() 함수로 구성됩니다. 일단은 간단하게 findAnswer() 함수에서 서버로부터 메시지를 계속 전달 받되, 첫 번째 문자가 “N"인 라인을 만나면 NC의 값을 파싱하여 변수에 담는 것까지 작업해보았습니다.



  실행 결과는 위와 같습니다. 서버로부터 계속 데이터를 전달 받다가, “N=숫자 C=숫자의 형태를 만나면 이를 Javasplit() 함수를 이용해 숫자 정보만 파싱하는 것입니다. 이제 NC를 받아오는 모듈까지 작성했으므로 이를 확장시켜서 이분 탐색을 활용해 정답을 도출하도록 확장시키면 됩니다.


import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


private static Socket sock = null;

private static OutputStream out = null;

private static InputStream in = null;

private static BufferedWriter bw = null;

private static BufferedReader br = null;


public static void init() {

try {

sock = new Socket("pwnable.kr", 9007);

out = sock.getOutputStream();

in = sock.getInputStream();

bw = new BufferedWriter(new OutputStreamWriter(out));

br = new BufferedReader(new InputStreamReader(in));

} catch (Exception e) {

e.printStackTrace();

}

}


public static void close() {

try {

bw.close();

br.close();

sock.close();

} catch (IOException e) {

e.printStackTrace();

}

}


public static int findAnswer() {

    try {

    // 서버로부터 메시지를 전달 받습니다.

    String line = null;

        while((line = br.readLine()) != null){

        // 전달 받은 메시지를 출력합니다.

        System.out.println(line);

        // 첫 번째 문자가 'N'인 경우 문제 풀이가 시작된 것입니다.

        if(line.length() != 0 && line.charAt(0) == 'N') break;

        }

        // N과 C를 파싱하여 각각 숫자로 변환합니다.

        int N = Integer.parseInt(line.split("N=")[1].split(" C")[0]);

        int C = Integer.parseInt(line.split(" C=")[1]);

        int up = N - 1;

        int down = 0;

        

        while(down <= up && C > 0) {

            int middle = (up + down) / 2;

            String message = "";

            for(int i = down; i <= middle; i++) {

            message += i + " ";

            }

            // 서버로 확인하고자 하는 코인들의 번호를 전달합니다.

            System.out.println("보낸 값: " + message);

            bw.write(message);

            bw.newLine();

            bw.flush();

            C--;

            // 서버로부터 결과 정보를 전달 받습니다.

            line = br.readLine();

            System.out.println("받은 값: " + line);

            int next = Integer.parseInt(line);

            // 10으로 나누어진다면 해당 범위에 가짜 코인이 없습니다.

            if(next % 10 == 0) {

            down = middle + 1;

            }

            // 그렇지 않다면 해당 범위에 가짜 코인이 있습니다.

            else {

            up = middle - 1;

            }

        }

        // 아직 확인할 수 있는 횟수가 더 남았다면 다 쓸 때까지 전송합니다.

        if(C > 0) {

            bw.write(down + "");

            bw.newLine();

            bw.flush();

        }

        // 결과적으로 찾은 가짜 코인의 번호는 down에 담겨있습니다.

        return down;

    } catch (Exception e) {

    e.printStackTrace();

    }

return -1;

    }


public static void main(String[] args) {

try {

init();

// 게임이 여러 차례 존재하므로 무한정 실행시킵니다.

while(true) {

int bottom = findAnswer();

String message = bottom + "";

System.out.println("가짜 코인 발견: " + message);

// 한 차시 게임에서 찾은 답을 전송합니다.

            bw.write(message);

            bw.newLine();

            bw.flush();

}

} catch (Exception e) {

System.out.println(e);

}

}

}



  결과적으로 총 100차례의 게임이 빠르게 수행되고 플래그 값으로 b1NaRy_S34rch1nG_1s_3asy_p3asy를 찾았습니다. 이를 입력하면 정답 처리를 받게 됩니다.

728x90
반응형

Comment +0

728x90
반응형

자바(Java)를 이용해 문제풀이 서버와 통신하기

나동빈

 

  일반적으로 시스템 해킹을 공부하기 위한 각종 포너블(Pwnable) 사이트나 실제 CTF 대회에서는 Netcat 모듈을 이용해 문제풀이 서버와 통신하는 경우가 많습니다. 또한 실시간으로 서버와 상호작용을 해야 하는 경우가 많기 때문에 실제로 문제를 풀 때는 파이썬(Python)을 이용해서 서버와 통신하는 모듈을 구현하곤 합니다. 하지만 저와 같이 국내 개발자 출신의 해커들은 자바(Java)가 더 익숙한 경우가 많기 때문에 자바를 이용해 문제를 푸는 방법에 대해 소개하고자 합니다.

 

  먼저 기본적인 Netcat(nc) 명령어를 이용해 서버와 통신하는 화면은 다음과 같습니다. 저는 윈도우 운영체제를 이용하고 있어서 별도의 Netcat 프로그램을 환경변수로 설정했습니다.



  pwnable.krcoin1 문제를 풀기 위해 접속한 모습은 위와 같습니다. 어차피 nc 모듈도 서버와 소켓 통신을 이용해 메시지를 주고받는 프로그램에 불과하기 때문에 어떠한 프로그래밍 언어를 이용하던지 간에 똑같은 기능을 구현할 수 있습니다.

 

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

 

public class Main {

 

public static void main(String[] args) {

    try {

      Socket sock = new Socket("pwnable.kr", 9007);

      InputStream in = sock.getInputStream();

      BufferedReader br = new BufferedReader(new InputStreamReader(in));

      String line = null;

      while((line = br.readLine()) != null){

        System.out.println(line);

      }

      br.close();

      sock.close();

    } catch (Exception e) {

    System.out.println(e);

    }

  }

}

 

  따라서 위와 같이 Java에서 Socket 통신 기본 라이브러리를 활용해서 동일한 기능을 구현할 수 있습니다. 프로그램의 실행 결과는 다음과 같습니다. 저는 이클립스(Eclipse) 개발환경에서 실행해보았습니다.



  요약하여 설명하자면 서버로부터 어떠한 명령어를 기다리고자 하는 경우에는 두 개의 라이브러리 InputStreamBufferedReader를 이용해 서버로부터 특정한 메시지를 전달받을 수 있습니다. 구체적 동작 과정을 살펴보면 InputStream을 이용해 서버로부터 전달 받은 메시지 스트림(Stream)을 일정 크기의 버퍼(Buffer)에 지속적으로 담아서 문자(Character) 형태로 읽어들여 String 객체에 문자열 데이터를 담은 것입니다. 이 때 이렇게 버퍼를 쓰는 이유는 매 번 CPU 자원을 활용하지 않고 일정 주기로 CPU 자원을 활용하도록 해서 프로그램의 효율성을 높이는 것입니다.

 

그렇다면 서버로 어떠한 데이터를 전달하고자 할 때는 어떻게 할 수 있을지 알아봅시다. 직관적으로 생각했을 때 InputStreamBufferedReader의 반대인 OutputStreamBufferedWriter를 쓰면 될 것 같다는 생각이 드실 겁니다. 결과적으로 다음과 같이 소스를 작성할 수 있습니다.


import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


public static void main(String[] args) {

        try {

            Socket sock = new Socket(특정한 서버, 포트 번호);

            InputStream in = sock.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(in));

            OutputStream out = sock.getOutputStream();

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));

            String line = null;

            while((line = br.readLine()) != null){

            System.out.println(line);

            if(line.contains("Enter your name:")) break;

            }

            bw.write("Dongbin, Na");

            bw.newLine();

            bw.flush();

            bw.close();

            br.close();

            sock.close();

        } catch (Exception e) {

            System.out.println(e);

        }

}

}


  위 소스코드는 특정한 서버에 접속해서 메시지를 계속해서 전달 받다가 “Enter your name:"이 포함된 문장을 만나면 더 이상 서버로부터 데이터를 전달받지 않고 "Dongbin, Na"라는 메시지를 서버로 전송하는 것입니다. 실제로 이러한 방식으로 서버와 클라이언트 통신을 진행할 수 있습니다. 물론 일반적으로 서버로부터 들어오는 데이터가 정형화되어있지 않다면 split(), contains(), substring() 등의 함수를 이용해서 메시지에서 필요한 내용만 파싱하여 사용할 필요도 있습니다.


728x90
반응형

Comment +0