Wargame/Pwnable.kr

[pwnable.kr] blackjack - 1 pt

2018. 5. 1. 20:49


이번에 풀어볼 녀석은 이녀석입니다.



문제가 기네요.. 백만장자에게 flag를 주겠다는 것 같습니다.

nc로 접속해보면 blackjack 게임을 할 수 있구요


돈 $500을 주고 베팅해서 컴퓨터를 이기면 돈을 받고 지면 돈을 잃습니다.


문제의 blackjack소스는 https://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html

저 url에 있습니다.


취약점을 여러군데에서 발견할 수 있는데...


먼저 betting 부분입니다.


 


보시면 알겠지만....

현재 가진 돈보다 베팅금액이 많다면 다시 한번 Bet을 입력하라고 나오는데...

이게 반복문이 아니고 다시 한번만 묻고 이번에는 bet을 리턴해주기 때문에 한번 cash보다 많은 금액 입력햇다가 다시 입력하면

그걸 받아들입니다.


그래서 아래와 같이 아슬아슬하게 int형의 범위를 안넘는 돈을 배팅하면 한번 거절했다가 betting을 받습니다.



여기서 이제 이기면 돈이 저만큼 들어옵니다.



예이... 이기니까 플래그도 주넴...


또 다른 취약점은 bet한 금액의 부호를 검사하지않는다는 건데요.

만약 제가 마이너스 금액 -10000을 배팅했다가 지게되면


cash = cash - (-10000);

이 되서 결국 플러스가 됩니다.


 





굿뜨...

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

[pwnable.kr] bof- 5 pt  (0) 2018.05.09
[pwnable.kr] collision - 3 pt  (0) 2018.05.02
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27

[pwnable.kr] cmd1 - 1 pt

2018. 5. 1. 15:21

이번에 풀 녀석은 이놈!



문제는 아래와 같다. 먼저 접속!





문제의 소스파일은 시작하면 PATH 환경변수를 망가뜨리고, argv[1]에서 flag, sh, tmp등을 필터한다.

필터후에 system으로 argv[1]을 실행하는데, PATH가 망가졌으므로 절대경로로 명령값을 넣어주면 된다.


원래 SHELL을 사용할때 명령어를 입력하면 PATH 환경변수에 지정된 경로에서 입력받은 명령과 같은 파일을 찾아 exec 시스템콜로 호출하는데

여기서 PATH를 망가뜨려주므로 cat을 사용하려면 /bin/cat으로 쓰면 된다.


flag를 필터하므로 fl*으로 출력하도록 해보았다.



잘 출력되었다.... 뭐지; 이렇게 쉬운 문제인가;



뭔가 좀 아닌것 같아서 다른 사람들은 어떻게 풀었는지 검색해보니

심볼릭 링크나 환경변수를 이용해서 문제를 풀었다. 심볼릭링크는 /tmp에 폴더 및 파일을 생성해 심볼릭링크를 걸어 해결하는 거고


환경변수 이용하는것이 이번 문제에서 의도한 것같다.

$ export x=/home/cmd1/flag

$ ./cmd1 "/bin/cat \$x"



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

[pwnable.kr] collision - 3 pt  (0) 2018.05.02
[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27
[pwnable.kr] random - 1 pt [Toddler's Bottle]  (0) 2018.04.25

[pwnable.kr] shellshock - 1 pt

2018. 5. 1. 00:10


이번에 풀어볼 녀석은 이녀석이다. 이름은 shellshock



bash에 대한 shocking 뉴스가 있다고 한다. 검색해보면 쉘쇼크(ShellShock)라는 취약점에 대해서 알 수 있다.


 쉘쇼크(Shell-Shock) (CVE-2014-6271)

2014년 9월 24일, GNU Bash 환경변수를 통한 코드 인젝션 취약점이 보고되었다. 이른바 ‘쉘쇼크(ShellShock)’로 불리는 해당 취약점환경변수를 이용하여 인젝션을 시도할 수 있어 공격자는 악의적인 명령이 포함된, 특별하게 조작된 환경변수를 사용하여 해당 취약점을 이용해 악의적인 행위를 수행할 수 있다.


저 쉘쇼크에 대해 이해하기 위해선 먼저 Shell에 대해 잘알아야한다. 여기 블로그에서 잘 설명되어 있어서 한번 가서 보고오는것도 좋다.

[unix/linux] shell(쉘)을 이해하자


일단 bash 취약점에 대해 간단히 설명가능한 명령어가 있는데 아래와 같다.


$ ​env x='() { :;}; echo vulnerable' bash -c "echo test"


간단히 설명하면 bash에는 환경변수라는 것이 있는데, bash에서는 이 환경변수에 값이 아닌 함수를 등록하고 

bash -c [변수명] 과 같은 방법으로 변수에 등록된 함수를 실행시킬수 있다.


한줄에 써져있어 잘 모를수있어 풀어쓰면 밑과 같다.


1    '( )

    :;

   }

4    echo vulnerable'

   bash -c "echo test"


우리가 흔히쓰는 함수의 선언과 다를게 없다. 

그럼 여기서 무엇이 문제점(취약점)이냐? 하면 다시 위의 명령을 보자

먼저 정상적인 동작이라면 함수정의가 끝나면서인 (){ }; 여기까지이다. 

그러므로 그 뒤에 echo 명령이 있는 부분은 원래라면 무시되거나 에러가 나면서 실행이 안되어야한다.


그러나 실제로는 bash가 함수를 실행하기 위해 파싱하는 과정에서 중괄호의 끝인 }와 ' 사이에 있는 명령(echo)을 실행시켜 버려

그 뒤에 부분이 전부 실행된다는 것이다.



여기에 있는 bash는 취약점있는 shellshock공격이 가능한 bash쉘이라는것이 확인되었다.

그러면 이제 shellshock.c의 내용을 통해 shellshock파일이 어떤 동작을 하는 지 보자.



setresuid와 setresgid 둘다주고 setuid가 걸려있으므로 root권한으로 실행될것이란것을 알 수 있다.

사용하는 bash는 취약점이 있는 bash이다. 프로그램은 system함수로 /home/shellshock/bash -c 'echo shock_me'를 실행하는데

저 bash가 실행될때 실행되는 함수뒤에 echo함수의 정의를 새로해두면 system함수에 의해 실행되는 subshell은 echo를 실행하면 cat flag를 실행하게 된다.


env x='() { :; }; echo(){ cat flag; }' /home/shellshock/shellshock



이렇게 플래그를 획득할 수 있다.


참고한 블로그 : 

http://operatingsystems.tistory.com/entry/Shellshock-CVE20146271

http://blogger.pe.kr/300





+다른 사람 writeup을 보고 알았는데

그냥 env x='() { :;}; /bin/cat flag' ./shellshock 로 해도 된다. 생각해보니 그렇다 ㅇㅇ;

그런데 이상하게 segmentation fault가 뜬다



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

[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27
[pwnable.kr] random - 1 pt [Toddler's Bottle]  (0) 2018.04.25
[pwnable.kr] fd - 1 pt [Toddler's Bottle]  (0) 2018.04.24

[pwnable.kr] mistake - 1 pt

2018. 4. 27. 01:21


이번에 풀어볼 문제는 이 놈이다.


mistake!!



문제를 한번 살펴보자, 대충 요약, 의역해보면


우리는 모두 실수를 저질렀다. (복잡한 기술은 필요없음)

실제 사건에 기반한 문제다.


힌트 : 연산자 우선순위 


라는 것같다. 일단 접속해보자.



password와 flag가 보이고, 둘다 지금 접속된 계정의 권한으로는 볼 수 가 없다.


볼 수 있는 것으로는 mistake.c 와 실행할 수있는 mistake가 있다.

일단 mistake.c의 내용은 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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;
}
 
 
cs


프로그램은 코드만 대충 봣을때는

password에서 10자 읽어와 pw_buf에 저장하고, 0~20초 정도 대기후

사용자로부터 문자열을 받아 pw_buf2에 저장하고, 문자열 전체에 xor 0x1 을 수행한 후


strncmp로 pw_buf와 pw_buf2가 같다면 flag를 출력해주는 방식이다.


그냥 겉으로봤을 때는 위와 같은 흐름대로 프로그램이 실행될것으로 보이나... 아니였다.

힌트가 연산자 우선순위인데(처음에는 strncmp 부분에서 무언가 있는가 해서 시간날때마다 살펴보았으나, 결국 삽질만 한것이였다.)

프로그램이 실행되고 초기에 if(fd=open("/home/mistake/password",O_RDONLY,0400< 0)을 수행하는데

여기서 연산의 우선순위가 '='연산자보다 '<'연산자가 높기때문에 fd=(open("~",O,0400< 0)같이 된다. 즉 open(~,~,~)<0이 먼저 수행되어 false(0)라는 결과가 나와 fd=0이 된다.


그래서 결국 sleep(time(0)%20); 후에 아래 if문의 read를 만나게되고 read(fd,pw_buf, pw_LEN)에서 fd가 0이므로 stdin으로 입력을 받아 pw_buf에 저장되게 된다. 이것이 뜻하는 것은 sleep()로 대기한 후 우리가 pw_buf값을 임의로 입력할 수 있다는 것이다.


pw_buf의 값과 pw_buf2의 값 모두 입력할 수 있고, pw_buf2는 xor 0x1를 수행한 후 pw_buf와 비교하게 되니, 

XOR연산의 특성상 pw_buf2 XOR 0x1 한 값을 pw_buf로 넣으면 XOR 0x1을 수행하면 0x1이 소거되어 둘이 같은 값이 될 것이다.


pw_buf2의 값으로 AAAAAAAAAA을 넣는다고 하면 pw_buf에는 "A" XOR 0x1한 @@@@@@@@@@@을 넣으면 될 것이다.


이제 한번 넣어보자.



와우...!! 풀었다! 잘했다 굿....!!


Mommy, the operator priority always confuses me :(



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

[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] random - 1 pt [Toddler's Bottle]  (0) 2018.04.25
[pwnable.kr] fd - 1 pt [Toddler's Bottle]  (0) 2018.04.24


이번에는 이놈을 잡을 겁니다.


저번과 마찬가지로 1pt



저번에는 Mommy였는데, 이번에는 Daddy한테 배우는 모양


일단 접속.



이번에도 마찬가지로 random에 setuid가 걸려있어서 저 random을 공략하면 될 것 같다.


소스코드를 확인해보자


#include <stdio.h>
 
int main(){
    unsigned int random;
    random = rand();    // random value!
 
    unsigned int key=0;
    scanf("%d"&key);
 
    if( (key ^ random) == 0xdeadbeef ){
        printf("Good!\n");
        system("/bin/cat flag");
        return 0;
    }
 
    printf("Wrong, maybe you should try 2^32 cases.\n");
    return 0;
}
cs


코드를 보자. 먼저 rand() 함수를 이용해서 랜덤한 값을 random에 받는다.


그 후 사용자로 부터 key를 입력받고, key와 random값을 XOR하여 이 값이 0xdeadbeef 값이라면 flag를 출력해준다.


rand에 대해 간단히 알아보면...

함수의 원형

   int rand( void );    // 난수의 범위 : 0~RAND_MAX, RAND_MAX는 0x7fff이므로 결국 난수의 범위는 0~32767

   void srand( unsigned int seed );  // rand()의 시드값 설정


그러나 rand()함수만 사용하면 프로그램을 실행할때마다 항상 일정한 난수값이 나오므로(그러므로 난수가 아니다.) 

보통은 srand()와 함께 사용하여 시드값을 설정하여 항상 같은 난수열이 나오지않도록해준다. 

하지만 이 프로그램은 그러고 있지않으므로 항상 똑같은 값이 random에 저장되어 있을 것이다.


그러므로 random값이 무엇인지 알아내어 0xdeadbeef와 xor하면 어떤 key값을 넣어야하는지 알 수 있을것이다.


 
#include <stdio.h>
 
int main(){
        unsigned int random;
        random = rand();        // random value!
 
        unsigned int key=0;
        scanf("%d"&key);
 
        printf("%d\n"0xdeadbeef^random);
        return 0;
}
 
cs


위와 같은 소스를 짜서 어떤 값을 입력하면 되는지 알아내었다.


-1255736440이라는 값이 나오고 이것을 이제 입력해주면 된다.


시작은 Daddy로 하고... 엄마를 찾는거냐..

Mommy, I thought libc random is unpredictable...


libc random값은 예측할수 없는 것이 아니였다...(물론 seed를 준다면 또 모르지만, 그것도 seed값을 안다면 예측할수있다...)



플래그획득! 굿!


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

[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27
[pwnable.kr] fd - 1 pt [Toddler's Bottle]  (0) 2018.04.24



pwnalbe.kr 시작합니다~~~


먼저 fd라는 1pt문제부터 시작.



Mommy! what is a file descriptor in Linux?


* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link:

https://youtu.be/971eZhMHQQw


ssh fd@pwnable.kr -p2222 (pw:guest) 


file descriptor에 대한 문제같다. 먼저 ssh로 저 주소로 접속해보자.



와우... 접속이 되고 기본적인 설명이 되어있다. 일단 파일이 무엇이 있는지 살펴보자.



저런것들이 있고, CTF나 Wargame류에서 흔히 그러듯 flag가 있다. 

하지만 flag의 읽기권한이 우리에겐 없으므로 읽는 것은 불가능하다.


여기서 fd를 보면 setuid가 걸려있어 프로그램을 실행하면 fd_pwn의 권한으로 일을 하게된다.

저 fd가 무엇을 하는지 알기 위해 fd.c를 보자.


(그전에 https://youtu.be/971eZhMHQQw에서 scp를 이용해서 파일을 내 서버로 가져오는 법을 배웠다.)


scp -P2222 fd@143.248.249.64:fd .

scp -P2222 fd@143.248.249.64:fd.c .


만약 바이너리파일만 주어지면 이렇게 내 서버로 가져와서 사용해보는 것도 나쁘지않아보인다.


그럼 한번 fd.c의 내용을 보자


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
 
}
 
cs


buf를 전역변수로 두고, 첫번째 인자로 받은 값을 atoi로 정수로 바꾸어 -0x1234를 한후 fd에 넣어준다.


그리고 read(fd, buf, 32);를 수행하여 32값을 읽어와 buf에 저장해준다. 먼저 read에 대해 알아보자.


man read



함수원형

  #include <unistd.h>

  ssize_t read(int fd, void *buff, size_t nbytes);


리턴값

  반환 값: 읽어온 바이트 수, 실패 시 -1, 파일의 끝에서 시도하면 0


여기서 read의 첫번째 인자 fd : 파일 서술자

두번째 인자는 *buff  : 읽어와서 저장할 버퍼

새번째 인자는 nbytes : 읽을 수 있는 최대 byte의 수


이다. 첫번째 인자로 들어가는 fd는 int형인데, 이는 파일 디스크립터가 C int 타입으로 표현되기 때문이다. (최대값은 1024이지만 1048576번까지 설정가능하다한다)

이 때, 0,1,2 번은 사전에 배정되어 있고 각각 아래와 같다. 


0 : 표준 입력 (stdin) / 1 : 표준 출력 (stdout) / 2 : 표준 오류 (stderr)


따라서 실제 하나의 파일을 생성하게 되면 “3번” 부터 File Descriptor가 부여된다고한다.

보통 사용할때 fd = open("path/", option)으로 먼저 fd에 번호를 부여한후에 read로 읽는게 대부분이다.


그러므로 fd값을 0으로 주면 stdin을 이용하여 buf에 우리가 원하는 값을 입력할 수 있게된다.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
 
}
 
 
cs


자 이제 문제로 돌아와서 코드를 한번 살펴보자.

fd를 설정한 후, len = read(fd, buf, 32); 를 통해 buf에 최대 32자를 받은후


strcmp를 통해 "LETMEWIN"이라는 문자열과 비교한다. 만약 같다면 0을 리턴하므로 if문이 실행되고

good job이라는 메세지와 함께 system("/bin/cat flag");를 수행하는 모습을 볼 수 있다.


fd는 setuid가 걸려 fd_pwn권한으로 실행되어 flag를 볼 수 있을 것이다.


그러므로 우리는 fd=0으로 만들어주기만하면 된다.

fd = atoi(argv[1]) - 0x1234이고, 0x1234는 4660이므로 ./fd 4660을 실행하면 우리가 원하는 값을 buf에 표준입력(stdin)으로 넣어줄수 있다.



mommy! I think I know what a file descriptor is!!


굿!!




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

[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27
[pwnable.kr] random - 1 pt [Toddler's Bottle]  (0) 2018.04.25

+ Recent posts