#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;

}



input의 코드이다. 보아하니 Stage를 하나씩 해결해가면서 마지막 Stage 5를 해결하면 될 듯 하다. 이번 문제는 전부 로컬 ubuntu 16.04LTS 로 복사해서 한 후 서버로 옮겨갔다.


0x01. Stage 1

우선 Stage1을 보자.

// 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");

//argv -> argv에 관한 문제이다.

if(argc != 100) return 0;

argv를 100개 넣어줘야만 한다.


if(strcmp(argv['A'], "\x00")) return 0;

argv의 'A'번째, 즉 0x41번째, 65번째 가 0x00이어야 한다.


if(strcmp(argv['B'], "\x20\x0a\x0d")) return 0;



argv의 'B'번째, 즉 0x42번째, 66번째 가 0x200a0d이어야 한다.



pwntool을 이용하기 위해 py파일을 만들자


argv를 여러개 전달하기 위해 pwntools 의 process함수를 볼 필요가 있다.


classpwnlib.tubes.process.process(argv=Noneshell=Falseexecutable=Nonecwd=Noneenv=Nonestdin=-1stdout=<pwnlib.tubes.process.PTY object>stderr=-2close_fds=Truepreexec_fn=<function <lambda>>raw=Trueaslr=Nonesetuid=Nonewhere='local'display=Nonealarm=None*args**kwargs)


argv=None인 부분에 []로 배열을 만들어서 넣어주면 될 듯 하다.



from pwn import *


r = process([ 'a',\

'a', 'a', 'a', 'a', 'a' ,'a' ,'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', '\x00', '\x20\x0a\x0d', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a' ,'a' ,'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a' , 'a', 'a'], executable='./input')


print(r.recv(1024))



이 정도면 stage1은 가볍게 통과할 것이다.



Clear!




0x02. Stage 2




// 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");



read(0, buf, 4);


fd가 0이므로 stdin이 들어간다. 가볍게 python으로 \x00\x0a\x00\xff를 보내주도록 하자



read(2, buf, 4);


아마 헤맨다면이 부분이지 않을까 싶다. 2는 stderr이며 여기서 값을 받아 buf에 넣어야 한다. stderr은 입력할 수 없으므로 입력할 방법이 없을 것 같다.


하지만, pwntools의 process를 다시한번 읽어보자.


classpwnlib.tubes.process.process(argv=Noneshell=Falseexecutable=Nonecwd=Noneenv=Nonestdin=-1stdout=<pwnlib.tubes.process.PTY object>stderr=-2close_fds=Truepreexec_fn=<function <lambda>>raw=Trueaslr=Nonesetuid=Nonewhere='local'display=Nonealarm=None*args**kwargs)


stderr=-2라는 것이 보인다. 이것을 이용해 보자.


stderr도 stdin과 stdout과 마찬가지로 fd이다. 이 fd는 파일을 열때 주어지는 고유번호라고 생각하면 된다.


pwntools의 process의 stderr에 fd값을 입력해주면 read(2, buf, 4);를 실행할때 해당 fd에서 받아올 것이다.


우선 파일을 만들고, \x00\x0a\x02\xff를 넣자


f = open("abcd", "w")

f.write('\x00\x0a\x02\xff")


해당 코드를 한번 실행하면 abcd라는 파일이 생기며, f에는 fd가 들어갈 것이다.

stderr=f로 해두자.



from pwn import *

import socket


context.log_level= 'debug'

f = open("abcd", "r")    //abcd파일을 r권한으로 염

// fd가 f로 들어감


r = process([ 'a',\

'a', 'a', 'a', 'a', 'a' ,'a' ,'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', '\x00', '\x20\x0a\x0d', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a' ,'a' ,'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a' , 'a', 'a'], stderr=f, executable='./input')


r.recv(1024)


r.send('\x00\x0a\x00\xff')

r.recv(1024)




이 정도면 stage2도 성공하지 않을까?



clear!




0x03. Stage 3



// env

if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;

printf("Stage 3 clear!\n");


코드가 간단하다. 그리고 그만큼 쉽다.


classpwnlib.tubes.process.process(argv=Noneshell=Falseexecutable=Nonecwd=Noneenv=Nonestdin=-1stdout=<pwnlib.tubes.process.PTY object>stderr=-2close_fds=Truepreexec_fn=<function <lambda>>raw=Trueaslr=Nonesetuid=Nonewhere='local'display=Nonealarm=None*args**kwargs)


얘를 다시한번 보자. env라는 인자가 있는것을 볼 수 있다. 이 인자에 dict형식으로 값을 넘겨주도록 하자. dict형식은 다음과 같이 선언한다.


envs = {'\xde\xad\xbe\x\ef":"\xca\xfe\xba\xbe"}



env의 인자로 envs를 넘겨주자.




Clear




0x04. Stage 4



// 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");



File을 사용하는 코드이다.

보아하니 \x0a라는 이름의 파일을 read권한으로 열어서 그 안에 든 4바이트가 \x00\x00\x00\x00이면 합격인 듯 하다.


위에서 stderr을 할때와 비슷하게 해결 가능할 듯 하다.


i = open("\x0a", "w")

i.write("\x00\x00\x00\x00")

i.close()



이 코드를 한번 실행시켜주면 원하는 파일이 생성될 것이다. 이 코드를 한번 실행 해 주면 Stage 4도 가볍게 클리어 할 듯 하다.



Clear!



0x05. Stage 5


대망의 마지막 Stage 5이다.


// 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");




으.. 역시 제일 길다. 소켓이니 하나하나 따져서 가보자

먼저 ip와 port에 대해 알아보자.


sd = socket(AF_INET, SOCK_STREAM, 0);


AF_INET 은 Ipv4 프로토콜이다.


saddr.sin_addr.s_addr = INADDR_ANY


INADDR_ANY는 서버의 IP주소를 자동으로 반환해 주는 함수로 define에 정의되어 있다. 즉 자기 자신의 서버니 IP로는 localhost나 127.0.0.1(loopback)를 사용하면 될 듯하다.


saddr.sin_port = htons( atoi(argv['C']) );


이말인 즉슨 입력했던 argv중에 C번째 즉 0x43번째 즉 67번째 입력한것이 포트번호가 된다. 뭐 점유되지 않았을 만한 포트를 고르자. 필자는 9999를 선택하였다.


pwntools에서 socket을 위해 제공하는 함수, remote를 사용하자.


m = remote('localhost', 9999)

로 호출해서

m.send를 사용해 \xde\xad\xbe\xef를 보내주자




from pwn import *

import socket


f = open("abcd", "r")

envs={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}


r = process([ 'a',\

'a', 'a', 'a', 'a', 'a' ,'a' ,'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', '\x00', '\x20\x0a\x0d', '9999', 'a', 'a', 'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', \

'a', 'a', 'a' ,'a', 'a', 'a', 'a', 'a' ,'a' ,'a', \

'a', 'a', 'a', 'a', 'a', 'a', 'a' , 'a', 'a'], stderr=f, env=envs, executable='./input')




print(r.recv(1024))


r.send('\x00\x0a\x00\xff')

r.recv(1024)

sleep(5)    //소켓 여는데 시간이 걸리므로 sleep을 써 주자.

m = remote('localhost', 9999)

m.send('\xde\xad\xbe\xef')


print(r.recv(1024))





CLEAR!




0x06. pwnable


이제 서버로 가자.


ssh -p 2222 input2@pwnable.kr 로 연결하자 비밀번호는 guest다.


/home/input2/ 에는 권한이 없어 쓸 수 없으니 tmp내에 아무 폴더나 만들고 사용하자.


Stage 2와 Stage4에 사용할 file들을 전부 python 코드를 사용해 만들자


input과 flag는 심볼릭 링크로 만들어두면 된다.


ln -s /home/input2/input ./input

ln -s /home/input2/flag ./flag


를 한 후 python으로 py파일을 실행해 주자.




CLEAR


'pwnable > Toddler's Bottle' 카테고리의 다른 글

[Toddler's Bottle] mistake  (0) 2018.05.28
[Toddler's Bottle] leg  (0) 2018.05.28
[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] passcode  (0) 2018.05.21
[Toddler's Bottle] flag  (0) 2018.05.21
블로그 이미지

천재보다는 범재

,