Shellsec

Fuld version: pop3 server til pop3 attack udfordring
Du ser lige nu en skrabet udgave af vores indhold. Se den fulde version med ordentlig formatering.
Dette stykke kode var oprindeligt en fuzzer til pop3 klienter, MUAs, men blev skrevet lidt om og brugt til serverdele af Juli 2015 - POP3 attack
Jeg ved ikke om nogen kan bruge det, men hvis i kan, så værsågod:
#!/usr/bin/env ruby
######################################################
## Product: POPFuzz v0.9
## Description: POP3 Client fuzzer
## Filename: popfuzz.rb (Must be executable)
## Author: iTick
## Ruby 1.9.3 +
## Started: 2015
## Usage: ./popfuzz.rb &
## OS: Any that supports Ruby
######################################################
# Purpose: Fuzzing client POP3 software by giving overly long responses to client commands etc.
# Must run as root to be able to listen on port 1023 or below.
# RFC 1081 POPv3 - obsolete
# Software is free for all to use.
# Known Bugs: Lots
# - Does not handle unknown commands at all.
# - Errorhandling missing but not really needed.
# - Released files should have no file extension.

## Required modules
require 'socket'

## Constants
PORT = 110                                                                              # Port to listen on
BANNER = "+OK POPFuzz v0.9 Server Ready (Shellsec.pw challenge)"
LISTEN_IP = "0.0.0.0"                                                   # Listen on INADDR_ANY
USERNAME ="postmaster"                                                  # Account for the client to use
PASSWORD = "Password1"                                                              # Password for the client to use
DOMAIN = "@shellsec.pw"                                                 # Domain to be presented in tne static email.

# Freeze constants. (SHould be done directly on the receiver, but it seems to not work in older versions of Ruby)
PORT.freeze
BANNER.freeze
LISTEN_IP.freeze
USERNAME.freeze
PASSWORD.freeze
DOMAIN.freeze

## Globals
$num_messages = 1                                                               # Number of messagens the server holds. Only one static is currently in place.

# *** Message 1 beginning ***
$msg1 = <<-END_OF_STRING
Return-Path: <no-reply@example.com>
Delivered-To: #{USERNAME}#{DOMAIN}
Received: from localhost (unknown [10.1.3.25])
        by harmony.local (Postfix) with ESMTP id 2CC88637E2
        for <#{USERNAME}#{DOMAIN}>; Wed, 13 May 2015 10:02:52 +0200 (CEST)
To: #{USERNAME}#{DOMAIN}
Subject: Hello
Message-ID: <0c4488f96a9385eed5de73fdfc366445@example.com>
Date: Wed, 13 May 2015 04:17:57 +0000
From: "Administrator" <no-reply@example.com>
Reply-To: no-reply@example.com
MIME-Version: 1.0
Content-Type: multipart/alternative; charset="UTF-8"; boundary="b1_5ecf0dd9b89ef5875fcc3e3ead24e962"
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; format=flowed; charset="UTF-8"
Content-Transfer-Encoding: 8bit

--b1_5ecf0dd9b89ef5875fcc3e3ead24e962
Content-Type: text/plain; format=flowed; charset="UTF-8"
Content-Transfer-Encoding: 8bit

Hey

The secret word is Hello.

--b1_5ecf0dd9b89ef5875fcc3e3ead24e962
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: 8bit

<html><head></head><body>
Hey<br />
The secret word is Hello.
</body></html>


--b1_5ecf0dd9b89ef5875fcc3e3ead24e962--
END_OF_STRING
# *** Message 1 ending ***


## Main
socket = TCPServer.new(LISTEN_IP, PORT)                 # Create a server socket (for listening)

# Main loop
loop do                                                                                 # Keep listening
        Thread.start(socket.accept) do |client|         # Handle accept() and spawn a thread for each client connection.
                client.puts BANNER                                              # Send banner to client according to RFC
                _state = "AUTHORIZATION"                                # Keep track of POP3 states
                _username = ""                                                  # This is to verify that the USER is set before accepting PASS

                # AUTHORIZATION state
                while _state == "AUTHORIZATION"
                        _message = client.gets                          # Read command from client
                        _command = ""                                           # Initialize command variable to ""
                        _argument = ""                                          # Initialize command argument to ""
                        _command, _argument = _message.chomp.split(/ /)         # Remove CR and LF if present and split the message into a command and an argument variable.

                        # USER
                        if _command == "USER" and _state != "AUTHORIZATION"                                                                     # USER command while not i AUTHORIZATION state. This should not happen.
                                client.puts "-ERR You should not do this now"
                        elsif _command == "USER" and !_argument and _state == "AUTHORIZATION"                   # USER was not followed by a username. State is correct.
                                client.puts "-ERR missing username"
                        elsif _command == "USER" and _argument == USERNAME and _state == "AUTHORIZATION"        # USER command matches an account. State is correct
                                _username = _argument
                                client.puts "+OK #{_username} is hanging out here"
                        elsif _command == "USER" and _argument != USERNAME and _state == "AUTHORIZATION"        # User command did not match requested account. State is correct.
                                client.puts "-ERR #{_argument} does not hang out here"
                        end

                        # PASS
                        if _command == "PASS" and _state != "AUTHORIZATION"                                                                     # PASS command given while not in AUTHORIZATION state. This should not happen.
                                client.puts "-ERR You should not do this now"
                        elsif _command == "PASS" and _username != USERNAME and _state == "AUTHORIZATION"        # PASS called before USER. State is correct.
                                client.puts "-ERR You should do USER first"
                        elsif _command == "PASS" and !_argument and _state == "AUTHORIZATION"                           # PASS was not followed by a password. State is correct.
                                client.puts "-ERR Passowrd missing"
                        elsif _command == "PASS" and _argument != PASSWORD and _username == USERNAME and _state == "AUTHORIZATION"      # Password was not correct. State is correct.
                                client.puts "-ERR Passowrd incorrect for that user"
                        elsif _command =="PASS" and _argument == PASSWORD and _state == "AUTHORIZATION"         # PASS matches. State is correct
                                client.puts "+OK #{_username} is now authorized"
                                _state = "TRANSACTION"                                                                                                                  # Authorization complete. Change state according to RFC. This will end current loop that handle the AUTHORIZATION state.
                        end

                        # QUIT
                        if _command == "QUIT"
                                client.puts "+OK leaving"
                                _state = "ENDING"
                        end

                        # AUTH FIXME: Not implemented
                        if _command == "AUTH"
                                client.puts "-ERR What are you doing?"
                        end

                        # CAPA FIXME: Not implemented
                        if _command == "CAPA"
                                client.puts "-ERR What are you doint?"
                        end
                end

                # TRANSACTION State
                while _state == "TRANSACTION"
                        _message = client.gets                                                                  # Read command from client
                        _command, _argument = _message.chomp.split(/ /)                 # Remove newlines ans split the message using space as a seperator

                        # STAT
                        if _command == "STAT" and _state != "TRANSACTION"               # STAT given in wrong state. This should not happen.
                                client.puts "-ERR You should not do that now"
                        elsif _command == "STAT" and _state == "TRANSACTION"    # STAT is given. State is correct.
                                client.puts "+OK #{$num_messages} #{$msg1.size}"
                        end

                        # LIST
                        if _command == "LIST" and _state != "TRANSACTION"               # LIST is given in wrong state. This should not happen.
                                client.puts "-ERR You should not do this now"
                        elsif _command == "LIST" and !_argument and _state == "TRANSACTION"             # LIST is given with no arguments. This is OK.
                                client.puts "+OK #{$num_messages} messages (#{$msg1.size} octets)"      # This should not be done this way. Make sure to calculate size of all messages in total if more than one exists.
                                client.puts "1 #{$msg1.size}"
                                client.puts "."
                        elsif _command == "LIST" and _argument == "1" and _state == "TRANSACTION"
                                client.puts "+OK #{_argument} #{$msg1.size}"
                        end

                        # RETR
                        if _command == "RETR" and _state != "TRANSACTION"
                                client.puts "-ERR You should not do this now"
                        elsif _command == "RETR" and !_argument and _state == "TRANSACTION"
                                client.puts "-ERR Message number missing"
                        elsif _command == "RETR" and _argument == "1" and _state == "TRANSACTION"
                                client.puts "+OK #{$msg1.size} octets"
                                client.puts $msg1
                                client.puts "."
                        end

                        # DELE (This does not actually delete the message, but returns a appropriate reply)
                        if _command == "DELE" and _state != "TRANSACTION"
                                client.puts "-ERR You should not do this now"
                        elsif _command == "DELE" and !_argument and _state == "TRANSACTION"
                                client.puts "-ERR no such message"
                        elsif _command == "DELE" and _argument == "1" and _state == "TRANSACTION"
                                client.puts "+OK message 1 deleted"
                        end

                        # NOOP
                        if _command == "NOOP" and _state != "TRANSACTION"
                                client.puts "-ERR You should not do this now"
                        elsif _command == "NOOP" and _state == "TRANSACTION"
                                client.puts "+OK"
                        end

                        # LAST FIXME: Not implemented
                        if _command == "LAST" and _state != "TRANSACTION"
                                client.puts "-ERR You should not do that now"
                        elsif _command == "LAST" and _state == "TRANSACTION"
                                client.puts "+OK 1"
                        end

                        # RSET
                        if _command == "RSET" and _state != "TRANSACTION"
                                client.puts "-ERR You should not do this now"
                        elsif _command == "RSET" and _state == "TRANSACTION"
                                client.puts "+OK maildrop has #{$num_messages} messages (#{$msg1.size} octets)"
                        end

                        # UIDL FIXME: Not implemented
                        if _command == "UIDL"
                                client.puts "-ERR not supported"
                        end

                        # XTND FIXME: Not implemented
                        if _command == "XTND"
                                client.puts "-ERR not supported"
                        end

                        # TOP FIXME: Not implemented
                        if _command == "TOP"
                                client.puts "-ERR not supported"
                        end

                        # QUIT
                        if _command == "QUIT"
                                client.puts "+OK leaving"
                                _state = "ENDING"
                        end
                end             # while

                # ENDING state (and all other states not handled - if any)
                client.close            # Close the client socket
        end             # End the thread that handle the current client
end     # Mail loop end

socket.close                            # Close the server socket. This should never happen. Use CTRL+C to stop the server.
Og ja, koden og det hemmelige ord til udfordringen er ændret. ;)
Kan huske jeg blev temmelig forvirret da jeg forsøgte at fingerprinte serveren til den udfordring der :)
(22-05-2016, 15:18)Spagnum Skrev: [ -> ]Kan huske jeg blev temmelig forvirret da jeg forsøgte at fingerprinte serveren til den udfordring der :)

Hehe. Du er da også nysgerrig af natur. Rigtig hacker mentalitet. ;)
(22-05-2016, 15:35)iTick Skrev: [ -> ]Hehe. Du er da også nysgerrig af natur. Rigtig hacker mentalitet. ;)

hehe det tager jeg så som et kompliment :)
(23-05-2016, 19:34)Spagnum Skrev: [ -> ]hehe det tager jeg så som et kompliment :)

Det er det også. :D
Jeg tror jeg skal teste email klienter igen snart. Se om jeg kan finde nogle bugs. :D