공유기 해킹에 대해 많은 관심을 갖고 있었으나 대학원 일정이 너무 바빠 시간을 못내고 있다가, 이러다가는 아무것도 못할 것 같다는 생각이 들어 조금 무리해서라도 연구를 시작하기로 결심..


대학원 생활 중 연구용 공유기를 구매할 기회가 생겨 ipTIME A604 구매하였고, 기기가 도착하자 마자 분해를 시작하였다.


 <그림 1> 연구용 공유기 1, ipTIME A604


분해한 공유기는 그림 2와 같다. 일반적으로 하드웨어 디버깅을 할 때 uart를 통한 serial통신을 이용해 디버깅을 하므로 혹시 가능하지 않을까 하는 마음에 uart로 먼저 접속해 보았다. 


원래 대학원 측에 uart to usb 모듈도 구매를 요청했었는데, 재고가 없어 제품이 도착하지 않았다. 그래서 갖고 있던 ARPI600[1]이라는 모듈을 활용해 uart to usb통신을 시도해 보았다.


<그림 2> 공유기를 분해 후 PC와 USB로 연결


사실 공유기를 처음 열어봤을 때 조금 당황했었다. 사실 좀 막연하게, 공유기 안에는 당연히 jtag 핀이 있지 않을까 하고 jtag to usb모듈을 구매신청하여 툴을 보유하고 있던 상황이었기 때문이었다. 그런데 공유기를 열어보니 덩그러니 핀 4개만 올라와 있었다..  

어떻게 할까 고민을 하다 그림 3의 모듈을 활용해 보기로 결정했다. 원래 그림 3의 모듈은 라즈베리파이를 이용한 암호프로토콜을 구현하기 위해 구매한 모듈인데, 보드 위에 USB TO UART라고 쓰여있는 것을 보고 내가 원하는 용도로 사용할 수 있지 않을까 하는 생각이 들어 아래와 같이 연결해 보았더니 통신이 성공적으로 수행되었다...(천만 다행..)


<그림 3> uart to usb 통신을 위해 활용된 ARPI600 모듈

기본적으로 USB to UART통신을 위해 설치해야 되는 드라이버가 있다. 이는 reference [3]에서 다운받을 수 있다. 드라이버를 컴퓨터에 설치한 후 장치관리자에 들어가면 그림 4와 같이 드라이버가 인식되는 것을 볼 수 있다. 


<그림 4 UART Driver 인식>


공유기와 Serial 통신을 수행하기 위해서 Putty를 이용한다.(Putty에 대한 자세한 설명은 [4]를 참조)


공유기에 접속하기 위해서는 아래와 같이 Putty를 설정하면 된다. 여기에서 Speed(baud) 는 1초 동안 몇 번 변조를 했는가를 나타내는 단위이다.(자세한 설명은 [5]를 참조) 여기에서 저자가 선택한 공유기 A604의 baud는 38400이므로[2] 해당 수치를 입력한다. 그리고 마지막 옵션 Flow control의 옵션은 None으로 설정하였다. 


<그림 5> Putty serial connection setting


다음과 같이 설정을 마치고 Open을 누르면 그림 6과 같이 공유기와 Serial 통신을 수행하는 화면을 볼 수 있다. 


<그림 6> Putty를 이용해 공유기와 serial 통신


사실 이렇게 연결만 하면 Id와 Pw를 입력하는 화면이 뜨거나, 쉘이 뜨지 않을까 하는 생각을 하고 있었다.. 그런데 내 예상과는 다르게 수행되는 메인프로세스가 있었고, 이 프로세스는 그림 7과 같이 ctrl+c 나 ctrl+z같은 커멘드로도 종료할 수 없게 되어있었다.(어찌보면 이게 당연한 것..)


<그림 7> "ctrl+c"과 "ctrl+z" 를 입력했을 때 출력된 결과


몇 년 전에는 ipTIME공유기에서 발견된 취약점으로 비교적 쉽게 공유기의 쉘을 띄울 수 있었던 것 같은데, 지금은 아마 다 막혀있지 않을까 생각이 든다.. 그래도 여기에서 포기할 수는 없으니, 다음번에는 공유기 펌웨어 분석을 통해 쉘을 띄우는 얻는 방법이 있는지 확인해봐야겠다는 생각을 한다.


사실 본 연구의 핵심은 공유기의 쉘을 따는 것 보다 공유기를 활용해 하고 싶은 것이 있어 시작되었다.. 다시 말해 공유기의 쉘을 띄우는 것은 주 목적이 아니다.. 그런데 쉘을 띄우는 것에만 시간을 너무 많이 투자하게 되지 않을까 하는 걱정이 든다.. @_@..


P.S. 하드웨어쪽 연구를 시작한지 얼마되지 않아 모르는 것이 상당히 많습니다.. 혹시 제 글에 잘못된 부분이 있다면 언제든 편하게 지적해 주시고 가르쳐 주시면 감사하겠습니다!


Reference:

1) About ARPI600 - http://www.waveshare.com/wiki/ARPI600

2) About ipTIME N8004R - https://wikidevi.com/wiki/IpTIME_N8004R#Serial

3) About driver for serial communication with USB to UARThttps://www.silabs.com/products/mcu/Pages/USBtoUARTBridgeVCPDrivers.aspx

4) About Putty - http://www.chiark.greenend.org.uk/~sgtatham/putty/

5) About serial communication speed(baud)http://forum.falinux.com/zbxe/index.php?document_srl=405830&mid=network_programming

'Wifi_Hacking' 카테고리의 다른 글

[#3] 공유기 분석 160209  (0) 2016.02.09
[#2]공유기 분석 151214  (2) 2015.12.14
Posted by Hugh_K
l
#Copyright 2015. Aitch_K all rights reserved.
#CIST, Korea University

import random


########################################################
# - 유전자를 초기화해주는 함수
# - mChromo에 6자리로 구성된 임의의 유전자를 생성하고
#   생성된 유전자를 mChromoGrp에 저장 
# - mChromo는 임의로 생성된 각각의 염색체
# - mChromoGrp는 chromo 염색체의 그룹
########################################################
def init(mChromo, mChromoGrp):
	for i in range(chromoNum):
		mChromo = list()
		for j in range(6):
			mChromo.append(random.randrange(0, 2))
		mChromoGrp.append(mChromo)
	print mChromoGrp

	return mChromo, mChromoGrp	


########################################################
# - 본 함수는 너무 낮은 Value를 출력하는 유전자를 제거하는 함수
#   여기서 Value가 낮다는 것은 유전자가 좋지 못하다는 것을 의미
# - 본 소스는 Val의 합이 80이하일 경우 염색체를 0으로 초기화
# - 이후 Random함수를 이용해 새로운 유전자를 생성
########################################################
def lowerValChecker(mChromoGrp, mChromoSumV, mChromoSumW):
	for i in range(chromoNum):
		mChromoSumV.append(0)
		mChromoSumW.append(0)
		for j in range(6):
			mChromoSumV[i] += (v[j][mChromoGrp[i][j]])
			
	for i in range(chromoNum):
		if mChromoSumV[i] < MIN_VAL:
			mChromoSumV[i] = 0
			mChromoSumW[i] = 0

	return mChromoGrp, mChromoSumV, mChromoSumW



########################################################
# - 본 함수는 한계 Weight를 넘어가게 만드는 유전자를 제거하는 함수
# - 기본적으로 Knapsack Problem에서는 가방이 감당할 수 있는 최고 무게를 제한함
# - 본 소스에서는 Wei의 합이 120이tkd일 경우 염색체를 0으로 초기화
# - 이후 Random함수를 이용해 새로운 유전자를 생성
########################################################
def overWeiChecker(mChromoGrp, mChromoSumV, mChromoSumW):
	optWei = 0 
	for i in range(chromoNum):
		for j in range(6):
			mChromoSumW[i] += (w[j][mChromoGrp[i][j]])

	for i in range(chromoNum):
		if mChromoSumW[i] > MAX_WEI:
			mChromoSumV[i] = 0
			mChromoSumW[i] = 0

		if mChromoSumW[i] > chromoSumW[optWei]:
			optWei = i
	
	print "vList: " + str(mChromoSumV)
	print "vMax: " + str(mChromoSumV[optWei])
	print "wList: " + str(mChromoSumW)
	print "wOpt: " + str(mChromoSumW[optWei])

	return mChromoGrp, mChromoSumV, mChromoSumW


########################################################
# - 본 함수는 적당한 유전자들을 대상으로 진화를 수행하는 함수
# - 위에서 언급된 “적당한”은 너무 낮은 Val를 출력하지 않으며, 한계 Wei를 초과하지 않는 것을 의미
# - 적당하지 않아 0으로 초기화된 유전자들은 본 함수를 통해 새롭게 재생성됨
# - 유전자의 진화는 살아남은 유전자들을 대상으로 첫 번째 유전자의 3자리, 두 번째 유전자의 3자리를 결합하여 수행
#   ex) [ 0 | 0 | 1 | 0 | 1 | 0 ] + [ 0 | 1 | 0 | 1 | 0 | 0 ]
#       = [ 0 | 0 | 1 |] + [| 1 | 0 | 0 ]
########################################################
def evolutionFunc(mChromoGrp, mChromoSumV, mChromoSumW, mNewGenRsv, mNewGen):
	for i in range(len(mChromoSumV)):
		mNewGenRsv.append(0)
		mNewGenRsv[i] = list()
		if mChromoSumV[i] != 0:
			for j in range(6):
				mNewGenRsv[i].append(mChromoGrp[i][j])
		else:
			for j in range(6):
				mNewGenRsv[i].append(random.randrange(0, 2))
		
	for i in range(len(mChromoSumV)):
		mNewGen.append(mNewGenRsv[i][0:3]+mNewGenRsv[(i+1)%chromoNum][3:6])

	mChromoGrp = mNewGen

	return mChromoGrp, mChromoSumV, mChromoSumW, mNewGenRsv, mNewGen



### main routine #################################################################
chromoNum = 20
MAX_WEI = 120
MIN_VAL = 80
GENERATION = 50

v = [[0, 20], [0, 30], [0, 66], [0, 40], [0, 66], [0, 80]]
w = [[0, 10], [0, 20], [0, 30], [0, 40], [0, 50], [0, 70]]

chromo = list()
chromoGrp = list()

chromo, chromoGrp = init(chromo, chromoGrp)

for i in range(GENERATION):
	print "=============================================================="
	print "Gen " + str(i+1) 
	chromoSumV = list()
	chromoSumW = list()

	newGenRsv = list()
	newGen = list()

	print "v = [20, 30, 66, 40, 66, 80]"
	print "w = [10, 20, 30, 40, 50, 70]"

	chromoGrp, chromoSumV, chromoSumW = lowerValChecker(chromoGrp, chromoSumV, chromoSumW)
	chromoGrp, chromoSumV, chromoSumW = overWeiChecker(chromoGrp, chromoSumV, chromoSumW)
	chromoGrp, chromoSumV, chromoSumW, newGenRsv, newGen = evolutionFunc(chromoGrp, chromoSumV, chromoSumW, newGenRsv, newGen)
	
	print "==============================================================\n"

'Implementation' 카테고리의 다른 글

[Network] Network node size estimation  (0) 2017.07.04
Posted by Hugh_K
l

[4pt]input

Pwnable.kr 2015. 8. 15. 20:05
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char* argv[], char* envp[]){	
		printf("Welcome to pwnable.kr\n");
		printf("Let's see if you know how to give input to program\n");
		printf("Just give me correct inputs then you will get the flag :)\n");

		// argv
		if(argc != 100) return 0;
		if(strcmp(argv['A'],"\x00")) return 0;
		if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
		printf("Stage 1 clear!\n");

		// stdio
		char buf[4];
		read(0, buf, 4);
		if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
		read(2, buf, 4);
		if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
		printf("Stage 2 clear!\n");

		// env
		if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
		printf("Stage 3 clear!\n");

		// file
		FILE* fp = fopen("\x0a", "r");
		if(!fp) return 0;
		if( fread(buf, 4, 1, fp)!=1 ) return 0;
		if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
		fclose(fp);
		printf("Stage 4 clear!\n");

		// network
		int sd, cd;
		struct sockaddr_in saddr, caddr;
		sd = socket(AF_INET, SOCK_STREAM, 0);
		if(sd == -1){
				printf("socket error, tell admin\n");
				return 0;
		}
		saddr.sin_family = AF_INET;
		saddr.sin_addr.s_addr = INADDR_ANY;
		saddr.sin_port = htons( atoi(argv['C']) );
		if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
				printf("bind error, use another port\n");
				return 1;
		}
		listen(sd, 1);
		int c = sizeof(struct sockaddr_in);
		cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
		if(cd < 0){
				printf("accept error, tell admin\n");
				return 0;
		}
		if( recv(cd, buf, 4, 0) != 4 ) return 0;
		if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
		printf("Stage 5 clear!\n");

		// here's your flag
		system("/bin/cat flag");
		return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define STDIN 0
#define STDERR 2 

int main(){
	int pipes_si[2];
	int pipes_se[2];

	if((pipe(pipes_si)==-1)) { printf("error in pipes_si\n"); exit(1); }
	if((pipe(pipes_se)==-1)) { printf("error in pipes_se\n"); exit(1); }

	if(fork()){
		dup2(pipes_si[0], STDIN); close(pipes_si[0]); close(pipes_si[1]);
		dup2(pipes_se[0], STDERR); close(pipes_se[0]); close(pipes_se[1]);

		char *i_argv[101] = {[0 ... 99] = "A"};
		i_argv[65] = "\x00";
		i_argv[66] = "\x20\x0a\x0d";
		char *i_env[] = { "\xde\xad\xbe\xef=\xca\xfe\xba\xbe" };

		FILE* fp = fopen("\x0a", "a");
		if(!fp) printf("fp error\n");
		if( fwrite("\x00\x00\x00\x00", 4, 1, fp)!=1) printf("fwrite error\n");
		fclose(fp);

		execve("/home/input/input", i_argv, i_env);
	}
	else{
		write(pipes_si[1], "\x00\x0a\x00\xff", 4);
		write(pipes_se[1], "\x00\x0a\x02\xff", 4);

		sleep(2);
		int client;
		struct sockaddr_in c_addr;
		client = socket(AF_INET, SOCK_STREAM, 0);
		if(client == -1){
			printf("socket error, tell admin\n");
			return 0;
		}
		c_addr.sin_family = AF_INET;
		c_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
		c_addr.sin_port = htons( 25005 );

		if( -1 == connect(client, (struct sockaddr*)&c_addr, sizeof(c_addr)))
		{
			printf("connection fail\n");
			exit(1);
		}
		write(client, "\xde\xad\xbe\xef", 4);
		sleep(3);
		close(client);
	}
}



Introduction:

  본 문제는 input이라는 프로그램과 커뮤니케이션 하며, 프로그램에서 요구하는 정보를 전달해 주는 문제. input이 요구하는 정답을 하나씩 넣어주면 스테이지가 하나씩 클리어 되고, 모든 스테이지가 클리어 되면 마지막에 flag를 출력함


How to solve:

  사실 어떻게 푼다는 것을 설명하는 것은 조금 어려울 것 같고, 그냥 프로그램이 요구하는 결과들을 하나씩 넣어주면 풀리게 된다. 시스템의 /tmp에 개인용 폴더를 만들고, 여기에서 익스플로잇을 작성해 돌렸다.

  문제를 풀며 한 가지 고민을 하게 만들었던 것이 있는데, 2번 째 스크립트의 20번 줄에 배열을  100칸만 선언하면 이상하게 부모프로세스에서 자식프로세스에서 실행된 input이라는 프로그램에 값을 넣어줘도 아무런 출력을 하지 않았다는 것이다. 이에 대한 것은 사실 아직도 잘 모르겠다.. (쨋든 101칸 이상을 선언하면 정상적으로 출력이 된다..) 

  이렇게 해서 stage 5 clear까지 화면에 출력되게 한 후 flag의 내용을 봐야하는데, 이를 위해 flag를 익스플로잇을 작성했던 개인용 폴더로 옮겨와야 한다. 하지만 권한 문제로 flag파일 본체를 개인 폴더로 옮겨올 수는 없으므로, 그림 1과 같이 심볼릭 링크를 이용하였다.


<그림 1> 익스플로잇을 작성하던 폴더에 flag파일의 심볼릭 링크를 만듬


<그림 2> 문제 해결


References:

1) 여러 시스템 함수들 - http://forum.falinux.com/zbxe/index.php?_filter=search&mid=C_LIB&search_target=title&search_keyword=recv&document_srl=441107

2) 어떤 분이 올려주신 답안 - http://rk700.github.io/writeup/2014/11/16/input/

'Pwnable.kr' 카테고리의 다른 글

[3pt]collision  (0) 2015.08.07
[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
Posted by Hugh_K
l