=====================================
< Hust Hacking Festival 2008 Report >



by hkpco(박찬암)
-------------------------------------
mail     - hkpco@korea.com
homepage - http://hkpco.kr/
group    - wowhacker team
=====================================
대회 참가 아이디 - newheart
-------------------------------------


"I am so sorry!"

대회 참여자는 모두 입상을 염두하고 중간중간 스크린샷을 찍으라는 명시가 되어 있지만 대회 기간 내내 너무 정신이 없던 탓에
한 장 밖에 찍지 못했습니다. 더군다나 단지 문제 구경을 위하여 살펴보다가 중간에 갑작스런 흥미로 뒤늦은 참여를 시작하게
된 덕분에 로그조차 남길 마음의 여유가 없었던 것 같습니다. 대회 기록이 많이 부족한 점 양해 부탁드립니다.



Part A - 1

특정 권한의 공지 글을 읽는 문제입니다. 레벨 변경 페이지가 존재하며 level 1 ~ 5 까지의 버튼이 있습니다.
level1이 되어야 패스워드가 담긴 글을 읽을 수 있는데, 현재 권한은 level5로 되어있기 때문에 이를 변경해야 합니다.
level2 까지는 간단히 변경이 가능하지만 level1은 관리자 만이 바꿀 수 있도록 구성되어 있기 때문에 다른 방법을 써야합니다.
일반 게시판이 보이길래 XSS로 관리자의 쿠키 혹은 세션을 가져오거나, CSRF 공격을 통하여 특정 계정의 레벨 변경을 시도하도록
공격해 보았지만 생각처럼 잘 되지 않았습니다. 다양한 시행착오 끝에 글 열람 시 다음과 같은 취약한 레벨 체크 코드를 유추해
낼 수 있었습니다.

--
if( $level < 2 ) {
	echo "인증 성공";
}

else {
	echo "인증 실패";
}
--


즉, 조건문의 잘못된 사용으로 인해 level이 1이 아니어도 그 이하인 경우 또한 글 열람이 가능해 지는 것입니다.
레벨 변경 페이지에서 웹 프록시를 통하여 현재 레벨을 1 이하로 설정한 뒤 글을 열람하였으며 패스워드를 획득할 수 있었습니다.





Part A - 2

텍스트 파일의 내용을 암호화 시켜주는 윈도우 바이너리와 이를 통해 암호화 된 패스워드 파일이 주어집니다.
특정 파일을 지정해 주면 암호화를 수행한 뒤 파일을 생성하여 암호문을 저장해 줍니다.
해당 암호화 루틴은 WriteFile() API를 역으로 추적하여 찾을 수 있었습니다. 다음과 같습니다(주석 추가).

--
.text:004014B9                 mov     ecx, ebp        ; ecx = file size
.text:004014BB                 sub     edx, ebp
.text:004014BD                 mov     edi, esi        ; edi = filesize
.text:004014BF
.text:004014BF loc_4014BF:                             ; CODE XREF: .text:004014D9j
.text:004014BF                 mov     al, [edx+ecx]   ; al = edx+ecx
.text:004014BF                                         ; ecx -> cnt
.text:004014BF                                         ; edx -> plain text
.text:004014BF                                         ;
.text:004014C2                 xor     al, bl          ; al = al ^ bl
.text:004014C4                 mov     [ecx], al       ; [ecx] = al;
.text:004014C6                 and     eax, 0FFh       ; eax = eax & 0xff
.text:004014CB                 shr     ebx, 8          ; ebx = ebx >> 8
.text:004014CE                 mov     eax, dword_4190B0[eax*4] ; eax = 4190b0[eax*4]
.text:004014D5                 xor     ebx, eax        ; ebx = ebx ^ eax
.text:004014D7                 inc     ecx             ; ecx++
.text:004014D8                 dec     edi             ; edi--
.text:004014D9                 jnz     short loc_4014BF ; al = edx+ecx
.text:004014D9                                         ; ecx -> cnt
.text:004014D9                                         ; edx -> plain text
--


xor을 수행하는것을 알 수 있으며 키 테이블은 다음 주소에 저장됩니다.

--
004190B0  00 00 00 00 96 30 07 77 2C 61 0E EE BA 51 09 99  ....?w,a咀Q.
.
.
.
--


암호 루틴을 C언어로 나타내면 다음과 같습니다.

--
#define MAX 4096

int main( void )
{
	int cnt;
	char ch;
	char str[MAX];
	char x_table[] =
        { 0x00, 0x00, 0x00, 0x00, 0x96, 0x30, 0x07, 0x77 ..
	...
	...
	...
	};

	unsigned long ebx = 0xA9D4FB79;

	for( cnt = 0 ; cnt < MAX ; cnt++ )
	{
		ch = str[cnt];

		ch = ch ^ (ebx&0xff);
		printf( "%c", ch );
		fflush(stdout);

		ebx = ebx >> 8;
		ch = x_table[ch*4];
		ebx = ebx ^ ch;
	}

	return 0;
}
--


xor은 같은 값으로 두 번 연산하게 되면 원래의 값으로 돌아오는 특징이 있습니다. 그래서 암호화 루틴을 유추하여 따로 복호화
루틴을 작성할 필요가 없습니다. 문제에서 주어진 파일을 복호화 하여 다음과 같은 패스워드를 얻을 수 있습니다.

--
[hkpco@hkpco HUST]$ ./hk_dec < password.txt
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&

          인증값 : 38d9587b9be0bd19be5dcb109f023504

!@%!@!@#^*!@#^(!@^(!@)^!@^!@^(!^$%($^#$^%(%#^(%#(^^#(@%#(^@%^
$(_(*^%$$%*()($#$%^&*()&^%$#$%^&*)(*&^%$%^&*((*&%$#$%^&*()(**
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
&&()*&&%&$#%^&*()$)*$%#^*#()@)@&$%^^@*($)$%#*$@&@*@)@$($@*$*
$#!$@#*@#*%%@#*@#$*$@#*$@#**&*(*(%#@!%!@#^#!@&$#@&#%*&@&@#$&
--





part A - 3

sQuest.jar 이라는 파일이 주어지며 자바로 작성된 서버 파일 같았습니다. 묶여있는 파일을 풀어서 자바 디컴파일러를 이용하여
코드를 살펴보던 도중 Client.class 파일에서 서버 접속 관련 정보를 얻을 수 있었습니다.

--
.
.
    public Client()
    {
        Socket socket1;
        socket1 = null;
        try
        {
            socket1 = new Socket("220.95.152.32", 9000);
            DataInputStream datainputstream = new DataInputStream(socket1.getInputStream());
            DataOutputStream dataoutputstream = new DataOutputStream(socket1.getOutputStream());
            BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in));
            (new ListenThread(datainputstream)).start();
            System.out.println("ready!!");
            do
            {
                String s = bufferedreader.readLine();
                if(s.equalsIgnoreCase("quit"))
                    System.exit(0);
.
.
.
--


서버 주소는 220.95.152.32 이며 포트는 9000 이었습니다. 서버에 접속하여 문자열을 보내면 특정 규칙으로 인코딩 되었는데
간단한 작업이기 때문에 수동으로 직접 각 문자에 매핑되는 문자를 찾아 내었습니다.

--
A -> C
B -> E
C -> H
D -> L
E -> K
.
.
V -> G
W -> B
X -> O
Y -> S
Z -> V
--


서버에 접속하면 인코딩 된 문자열로 예상되는 불규칙적인 7bytes의 스트링을 출력해 주는데,
이에 대한 평문을 다시 전송하면 어떠한 응답이 올 것으로 예상하고 다음과 같이 간단한 코딩을 하였습니다.

--
/*
	hkpco
	ChanAm Park
*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

int sock_conn( char **argv );
int err( char *msg );

int main( int argc , char **argv )
{
	int sockfd;
	char buffer[1024] = {0x00,};
	char *p = buffer;
	char table[] =
{'C','E','H','L','K','M','A','Y','T','D','X','R','Z',
'U','I','Q','N','P','J','F','W','G','B','O','S','V'};

	char key[16] = {0x00,};

	int cnt, cnt_in;

	if( argc < 3 ) {
		fprintf( stderr, "%s [server] [port]\n", argv[0] );
		return -1;
	}

	sockfd = sock_conn( argv );
	memset( buffer, 0x0, sizeof(buffer) );
	read( sockfd, buffer, sizeof(buffer) );
	printf( "%s\n", buffer );
	printf( "%s\n", p+7 );

	for( cnt = 0 ; cnt < 7 ; cnt++ )
	{
		for( cnt_in = 0 ; cnt_in < sizeof(table) ; cnt_in++ )
		{
			if( *(p+7+cnt) == table[cnt_in] ) {
				printf( "%c\n", 'A'+cnt_in );
				key[cnt] = 'A'+cnt_in;
			}
		}
	}
	printf( "key: %s\n", key );

	write( sockfd, key, strlen(key) );
	memset( buffer, 0x0, sizeof(buffer) );

	read( sockfd, buffer, sizeof(buffer) );
	printf( "last rcv: %s\n", buffer );

	close(sockfd);
	return 0;
}

int sock_conn( char **argv )
{
	int sockfd;
	struct sockaddr_in sock;
	struct hostent *host_st;

	sockfd = socket( PF_INET, SOCK_STREAM, 0 );
	if( sockfd < 0 )
		err( "socket()" );

	host_st = gethostbyname( argv[1] );
	if( host_st == NULL )
		err( "gethostbyname()" );

	bzero( sock.sin_zero, sizeof(sock.sin_zero) );
	sock.sin_family = AF_INET;
	sock.sin_port = htons(atoi(argv[2]));
	sock.sin_addr = *((struct in_addr *)host_st->h_addr);

	if( connect( sockfd, (struct sockaddr *)&sock, sizeof(sock) ) < 0 )
		err( "connect()" );

	return sockfd;
}

int err( char *msg )
{
	perror(msg);
	exit(-1);
}
--


실행한 결과 NEXT : solution 이라는 메시지를 전달받을 수 있었으며 이 때 전달한 평문이 인증 패스워드가 되었습니다.





Part A - 4

Ajax로 코딩 된 계좌 이체 페이지가 주어졌습니다. 그런데 웹 프록시로 확인해 본 결과 이체 버튼을 클릭하여도 이체 실패라는
경고창만 뜰 뿐 어떠한 submit도 이루어지지 않는 것을 알 수 있었습니다. 웹 페이지의 소스 코드를 보면 JScript.Encode가 되어
있기 때문에 원래의 소스 코드를 볼 수 없는데, 이는 다양한 툴이나 웹상에서 제공하는 디코딩 서비스를 이용하면 쉽게 복호화
할 수 있습니다. 계좌 이체 페이지의 submit 코드는 다음과 같습니다.

--
<FORM METHOD=POST ACTION="makgurlri.php" NAME="fInput">
<TR height="30">
<TD width="150">&nbsp;</TD>
<TD>&nbsp;</TD>
</TR>
.
.
<TR height="30">
<TD colspan="2" align="center"><INPUT TYPE="submit" VALUE="   입   력   " onClick="fcInputBtn();"></TD>
</TR>
</FORM>
--


버튼을 클릭하면 fcInputBtn();이 호출되며 해당 코드는 다음과 같습니다.

--
function fcInputBtn()
{
	document.fInput.action = 'chivasRegal.html';
	window.alert('이체 실패!');
}
--


기존 makgurlri.php에서 chivasRegal.html로 action을 바꾸어 주는것을 볼 수 있으며 실제 form의 action 또한 chivasRegal.html로
변경하여 submit되도록 수정하였습니다. 시간이 꽤 걸릴법한 복잡한 수식을 패스워드라고 출력해 주었으며 짧은 시간마다 바뀌어서
처음에는 문제에서 출력해 주는 복잡한 수식을 위한 문제 전용 계산 코드를 작성하였지만, 결국 이러한 작업이 필요하지 않다는
것을 알게되었습니다. 패킷 캡쳐 결과에서 패스워드를 출력해주는 페이지가 포함되어 있었으며 사용자에게 입력을 받는 인증
패스워드와 OPT 값은 아무 상관이 없는것으로 보였습니다. 패스워드는 "By doubting we come at the truth" 입니다.





Part B - 1

시, 분, 초, 밀리초를 표시해 주는 타이머가 각각 세 개가 존재하는 윈도우 바이너리가 주어졌습니다.
ollydbg로 살펴본 결과 각 시간 단위마다 다음과 같은 비교 구문을 거쳤습니다(ollydbg로 본 코드).

--
.
.
0040163E   > \BF 3C000000   mov edi, 3C                              ;  Default case of switch 004015EA
00401643   >  8B4E 70       mov ecx, dword ptr ds:[esi+70]
00401646   .  B8 74000000   mov eax, 74
0040164B   .  3BC8          cmp ecx, eax
0040164D   .  75 1D         jnz short timer.0040166C
0040164F   .  837E 74 70    cmp dword ptr ds:[esi+74], 70
00401653   .  75 17         jnz short timer.0040166C
00401655   .  837E 78 72    cmp dword ptr ds:[esi+78], 72
00401659   .  75 11         jnz short timer.0040166C
0040165B   .  3946 6C       cmp dword ptr ds:[esi+6C], eax
0040165E   .  75 0C         jnz short timer.0040166C
00401660   .  53            push ebx
00401661   .  53            push ebx
00401662   .  68 50304000   push timer.00403050                      ;  ASCII "You are around solution"
00401667   .  E8 D2040000   call <jmp.&MFC42.#1200>
.
.
.
--


타이머 시간의 비교 조건을 만족하면 다음과 같은 루틴을 거치게 됩니다(ida로 본 코드).

--
.text:00401280 sub_401280      proc near               ; DATA XREF: .rdata:004025ACo
.text:00401280
.text:00401280 arg_0           = dword ptr  4
.text:00401280
.text:00401280                 push    esi
.text:00401281                 mov     esi, ecx
.text:00401283                 push    edi
.text:00401284                 mov     edi, [esp+8+arg_0]
.text:00401288                 lea     eax, [esi+6Ch]
.text:0040128B                 push    eax
.text:0040128C                 push    3EDh
.text:00401291                 push    edi
.text:00401292                 call    ?DDX_Text@@YGXPAVCDataExchange@@HAAI@Z ; DDX_Text(CDataExchange *,int,uint &)
.text:00401297                 lea     ecx, [esi+70h]
.text:0040129A                 push    ecx
.text:0040129B                 push    3E8h
.text:004012A0                 push    edi
.text:004012A1                 call    ?DDX_Text@@YGXPAVCDataExchange@@HAAI@Z ; DDX_Text(CDataExchange *,int,uint &)
.text:004012A6                 lea     edx, [esi+74h]
.text:004012A9                 push    edx
.text:004012AA                 push    3EBh
.text:004012AF                 push    edi
.text:004012B0                 call    ?DDX_Text@@YGXPAVCDataExchange@@HAAI@Z ; DDX_Text(CDataExchange *,int,uint &)
.text:004012B5                 lea     eax, [esi+78h]
.text:004012B8                 push    eax
.text:004012B9                 push    3ECh
.
.
.
--


이전에 비교한 타이머의 시각들이 DDX_Text의 인자로 전달되는데, 함수 호출 순서가 아닌 두 번째 인자(에디트 컨트롤 ID) 순으로
아스키 코드를 정렬하면 "tprtlgyflsla" 이라는 문자열을 구할 수 있으며 이것이 패스워드가 됩니다.





Part B - 2

테트리스 게임이 주어지며, 게임 내에 포함 된 이미지대로 블럭을 쌓으면 패스워드를 나타내 주는것으로 보였습니다.
이는 현재 블럭 상태 정보를 저장하여 주는 메모리 영역을 그림대로 변경하면 되며 조건이 만족하였을 경우 패스워드가
짧은 시간 간격으로 한 문자씩 보여지기 때문에 블록 상태 정보를 따로 저장해 두지 않으면 순간적으로 지나가는 문자들을
놓칠 수 있으므로 이는 따로 저장해 두는 것이 좋습니다. 게임에서 주어지는 그림과 동일한 블럭의 상태 정보는 다음과 같습니다.

--
02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00
00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00
.
.
.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
--


인증키는 한 문자씩 나타나며 이를 나열해 보면 "BE5tP4SVxA5Lki18" 라는 패스워드를 구할 수 있습니다.





Part B - 3

라우터 접속 시 화면의 스크린 샷을 보여주며 이를 통해 패스워드를 알아내는것이 문제입니다.
root 계정 패스워드의 hash 값이 포함되어 있었고, 웹에서 제공되는 시스코 라우터 관련 크랙
서비스를 통하여 복호화 할 수 있었습니다. 패스워드는, "CAFEamericano!!"





Part B - 4

제로보드 게시판 3개와 비밀글 2개가 존재하는 페이지가 주어졌습니다.
비밀글은 write_ok.php에서 글 삭제 시 사용되는 $del_queX 변수를 조작하면 읽을 수 있으며
취약 부분의 php 코드는 다음과 같습니다.

--
@mysql_query("update $t_board"."_$id set headnum='$headnum',prev_no='$prev_no',next_no='$next_no',
child='$child',depth='$depth',arrangenum='$arrangenum',father='$father',name='$name',email='$email',
homepage='$homepage',subject='$subject',memo='$memo',sitelink1='$sitelink1',sitelink2='$sitelink2',
use_html='$use_html',reply_mail='$reply_mail',is_secret='$is_secret',
category='$category' $del_que1 $del_que2 where no='$no'") or error(mysql_error());
--


여기서 가장 마지막 부분의 $del_que1을 이용하여 공격을 시도하였습니다.
비밀글은 다음과 같이 볼 수 있습니다.

--
, memo=(select memo from zetyx_board_bOarD_1 where no=게시글번호) 
--


두 개의 비밀글 내용은 각각 "TABLE NAME", "721f214d3c9de37d58a5b38c55e651c7" 이었으며, 새로운 공격을 시도해야 하는것으로
보였습니다. 해당 취약점을 이용하여 information_schema db를 살펴본 결과 721f214d3c9de37d58a5b38c55e651c7 테이블을 포함하는
database 이름을 알아낼 수 있었습니다. 721f214d3c9de37d58a5b38c55e651c7 테이블을 포함하는 db 이름은 bIgBaNG 이며, 각각의
컬럼 이름은 P, A, SS, W, O, R, D 라는 것을 알 수 있습니다. 각 필드값은 다음과 같이 추출하였습니다.

--
, memo=(select P from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select A from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select SS from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select W from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select O from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select R from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
, memo=(select D from bIgBaNG.721f214d3c9de37d58a5b38c55e651c7) 
--


이렇게 구한 최종적인 문자열을 나열하면 "m@ket0d4yth3b3Std@y0fy0Url!F3"라는 패스워드를 구할 수 있습니다.





Part C - 1

네트워크상에 돌아다니는 패킷들을 캡쳐한 것으로 보이는 16진수 코드들이 주어졌습니다.
데이터 부분으로 추측되는 영역에 문자로 나타낼 수 있는 범위의 연속된 16진수들이 보였으며 이를 각각 문자로
변환하면 "PASS qksrkqtmqslek" 라는 문자열을 얻을 수 있습니다. 패스워드는, "qksrkqtmqslek" 입니다.





Part C - 2

게시판이 주어졌으며 빨강색으로 표시 된 "이게 정답이 맞나요??"라는 제목의 읽을 수 잆는 글이 존재하였습니다.
풀이는 간단한 XSS를 통하여 세션값을 훔쳐오는 방법으로 가능하였지만 상당히 오랜 시간이 걸렸던 문제로 생각됩니다.
XSS 취약성을 의도한 문제라면 자동화 작업을 해 놓아야 하는데 그렇지 못한것으로 보였으며 관리자 세션을 받기가 상당히
어려웠습니다. 그리고 일반 사용자도 글을 읽을 수 있었기 때문에 XSS를 통해 document.cookie 값을 저장해 놓은 파일에는
여러 세션이 뒤섞여 있었고 문제에서 관리자 세션을 일반 사용자의 세션과 동일한 형태로 만들어 놓았기 때문에 구분을 할
수가 없었습니다. 일반 사용자가 게시글을 읽을때 제한을 두거나 관리자 세션 형태를 조금 특별하게 구성해 두었더라면
하는 아쉬움이 많이 남았습니다. 아무튼, 게시글 수정 시 XSS 코드를 필터링 없이 삽입할 수 있는 취약점을 통하여 관리자
세션을 얻을 수 있었으며 해당 세션을 통해 게시글을 열람하여 패스워드를 획득할 수 있었습니다.





Part C - 3

패킷 캡쳐 파일인 aracode.pcap가 주어졌습니다.
wireshark를 통해 살펴본 결과 FTP 통신이 이루어 진것을 볼 수 있었고, 데이터가 오가는 과정을 따로 파일로 저장하여
패스워드가 저장된 압축 파일 두개를 만들 수 있었습니다. 하지만 압축 파일에 패스워드가 걸려있었기 때문에 이를 풀어야
했는데, 단순한 무차별 대입 공격으로는 알아낼 수 없었습니다. 그래서 Advanced ZIP Password Recovery라는 툴을 이용하여
사전 파일을 이용한 무차별 대입으로 압축 파일의 패스워드를 얻어냈습니다. 압축을 풀면 텍스트 파일 하나와 그림 파일 하나가
존재하는데, 텍스트 파일에 적힌 숫자를 그림 파일에서 보여주는 아스키 코드표에 대응하여 문자를 추출해 내면 "woongkang" 이라는
패스워드를 얻을 수 있습니다.





Part C - 4

문제를 위해 제작된 사이트가 주어졌습니다.
게시판의 파일 업로드 취약성을 이용하여 php3 등의 확장자로 php 파일을 업로드 한 뒤, 업로드 디렉토리를 유추해서 쉘 권한을
획득할 수 있습니다. 추가적으로 게시판의 db 정보가 담긴 파일을 읽으므로 인해 sql 정보 까지 모두 열람이 가능하였습니다.
하지만 서버 내의 정보를 아무리 뒤져도 레벨 패스워드와 관련된 데이터를 찾을 수 없어서 내부 서버에 취약점이 있는 것으로
판단하였습니다. ifconfig 명령을 이용하여 192.168.200.10라는 내부 서버를 찾을 수 있었지만 어느정도 살펴본 결과 현재 쉘을
획득한 서버와 동일한 서버라는것을 알 수 있었습니다. 그래서 오래전에 만들어 둔 ipscan 기능이 포함된 스캐너를 이용하여
내부 서버를 조사하였으며 192.168.200.11 라는 서버가 존재한다는 것을 알아낼 수 있었습니다. 다음은 포트 스캔 결과입니다.

--
hkscan -> portscan -> 192.168.200.11

loc-srv [135]
NoName [3389]
--


Windows의 RPC 서비스로 추측되는 135 포트와, 원격 제어를 위한 포트로 추측되는 3389 포트가 열려있는 것을 확인하였습니다.
포트 포워딩을 생각하였지만 다른 문제 풀이때문에 나중으로 미루었고, 이후에 포트 리다이렉션(생각했던것과 같은 의도)이라는
힌트가 제공되었습니다. 마지막 1시간을 남겨두고 사용자 계정에서 포트 리다이렉션을 수행하는 툴을 찾다가 실패하여 ssh 터널링을
시도하는 도중에 대회가 끝났습니다.