Skip to content

Commit

Permalink
Binary guide (#26)
Browse files Browse the repository at this point in the history
* Will continue tomorrow

* Will add descriptions tomorrow.

* Will finish tomorrow morning first thing.

* Will work on actual guide next.

* Optimised images with calibre/image-actions

* Update

* Optimised images with calibre/image-actions

* Ready for review.

* Optimised images with calibre/image-actions

---------

Co-authored-by: ninjeeter <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent 528449a commit cf57780
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 7 deletions.
9 changes: 9 additions & 0 deletions .vitepress/sidebars/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ export const conceptsSidebar: DefaultTheme.SidebarItem[] = [
},
],
},
{
text: "Backend",
items: [
{
text: "Dealing with Binary",
link: "/concepts/backend/binary",
},
],
},
{
text: "Essentials",
items: [
Expand Down
4 changes: 4 additions & 0 deletions .vitepress/sidebars/guides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export const guidesSidebar: DefaultTheme.SidebarItem[] = [
text: "Spawning a Process",
link: "/guides/components/spawning_process",
},
{
text: "Using Invalid UTF-8",
link: "/guides/components/utf",
},
],
},
{
Expand Down
Binary file added src/_images/raw_byte_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/_images/replaced_character.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/_images/rust_conversion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/_images/utf_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions src/concepts/backend/binary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Dealing with Binary Data

Caido plugins are written in JavaScript. When creating a plugin that must handle raw bytes, there are some caveats to be aware of as the backend language used by Caido is Rust.

## UTF-8 Encoding

For Unicode code points outside of the ASCII range <span style="color: #EBEBF599; font-style: italic">(U+0000 to U+007F)</span> multiple bytes are used.

When encoding code points into multibyte UTF-8 characters, the bytes are prefixed with specific bit patterns. The first byte prefix indicates how many bytes are used based on the number of leading '1's, and any subsequent <span style="color: #EBEBF599; font-style: italic">(continuation)</span> bytes begin with '10'.

<img alt="UTF-8 encoding chart." src="/_images/utf_chart.png" center/>

This can be an issue if you want to create a Caido plugin that sends invalid UTF-8 to a target server to see how it is processed.

For example, if your intention is to append the byte `\x85` (`[1000 0101]`) to a path:

1. JavaScript strings are encoded using UTF-16 (`[0000 0000 1000 0101]`).
2. When passed to Rust, it is encoded as UTF-8. To produce valid UTF-8, the prefix is added to satisfy the multibyte pattern rules:
- First byte `C2`: `[1100 0010]` <span style="color: #EBEBF599; font-style: italic">(prefix marks start of 2-byte sequence)</span>
- Second byte `85`: `[1000 0101]` <span style="color: #EBEBF599; font-style: italic">(continuation byte)</span>
3. Now, `[1100 0010 1000 0101]` is sent instead of the intended `[1000 0101]`.

::: tip
You can view this visually by listening for the request with Netcat:

`ncat -lvnp 5555`

<img alt="Unprintable character." src="/_images/replaced_character.png" center/>

You can view the bytes by piping `xxd` to display the hex dump:

`ncat -lvnp 5555 | xxd -g1`

<img alt="C2 85" src="/_images/rust_conversion.png" center/>
:::

## Preserving the Raw Byte

To avoid the conversion, the raw byte must be used instead. This can be accomplished by:

1. Taking a request and returning a UInt8Array byte stream:

`.getPath({raw: true})`

2. Creating a new array that includes the existing bytes and the added byte:

`let path = [...spec.getPath({raw: true}), 0x85]`

3. Setting the path to use this new array:

`spec.setPath(path)`

::: tip
Now, `C2` will not be prefixed:

`ncat -lvnp 5555 | xxd -g1`

<img alt="Just \x85." src="/_images/raw_byte_example.png" center/>
:::

## What's next?

To view the full script and an additional technique that can be used to set raw bytes in Caido plugins, click [here](/guides/components/utf.md).
2 changes: 1 addition & 1 deletion src/concepts/essentials/tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ Once the package is developed, the code is processed by the [Vite](https://vitej

The file related to the Vite build tool within the starterkit repository is:

- `vite.config.ts`: This file is a configuration file for customizing the build process. _View the [vite.config.js](https://v2.vitejs.dev/config/) documentation for more information.
- `vite.config.ts`: This file is a configuration file for customizing the build process. _View the [vite.config.js](https://v2.vitejs.dev/config/) documentation for more information._
6 changes: 5 additions & 1 deletion src/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

Here you will find explanations of the core concepts that underpin Caido plugins.

## Backend

- [Dealing with Binary Data](./backend/binary.md) - Using invalid UTF-8 in Caido.

## Essentials

- [Plugin Architecture](./essentials/package.md) - The structure of a plugin package.
- [Runtime](./essentials/runtime.md) - Javascript runtimes used by plugins.
- [Tooling](./essentials/tooling.md) - Tools for the ease of plugin development.
- [Runtime](./essentials/runtime.md) - Javascript runtimes used by plugins.

## Modules

Expand Down
4 changes: 3 additions & 1 deletion src/guides/components/request.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ By registering a command in the frontend, defining the command to make a backend

``` ts
import type { Caido } from "@caido/sdk-frontend";
import type { API } from "starterkit-plugin-backend";
import type { API } from "../../backend/src/index";

import "./styles/index.css";

export type CaidoSDK = Caido<API>;

Expand Down
144 changes: 144 additions & 0 deletions src/guides/components/utf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Using Invalid UTF-8

::: tip
For conceptual information regarding this guide - click [here](/concepts/backend/binary.md).
:::

## Using a Mutable Proxied Request

To use invalid UTF-8 in the path of a request that passes through Caido, follow these steps:

### /packages/backend/src/index.ts

First import the `SDK`, the interface used to interact with Caido.

``` ts
import { SDK } from "caido:plugin";
```

Then create a function that takes proxied requests using `onInterceptRequest` and converts them into mutable, un-saved `RequestSpec` objects using the `.toSpec()` method. Next, store the path as a byte array using the spread operator `...`, and append the desired raw byte using `[...spec.getPath({raw: true}), 0x85];`. Update the request with the modified path using `.setPath()` and send the request.

``` ts
export function init(sdk: SDK) {
sdk.events.onInterceptRequest(async (sdk, request) => {
const spec = request.toSpec();
let path = [...spec.getPath({raw: true}), 0x85];
spec.setPath(path);
await sdk.requests.send(spec);
});
}
```

## Using a Newly Created Request

To use invalid UTF-8 in the path of a `new RequestSpecRaw()` request, follow these steps:

### /packages/backend/src/index.ts

First, import the required dependencies. `SDK` is the interface used to interact with Caido. `DefineAPI` is used to structure the API: definining what methods or endpoints are available, the parameters those methods accept and what types of values they return. `RequestSpecRaw` is an object class that is used to create a request in raw byte format.

``` ts
import { RequestSpecRaw } from "caido:utils";
import { SDK, DefineAPI } from "caido:plugin";
```

Next, define a function that will convert the path string into an array of bytes.

``` ts
function stringToUint8Array(str: string): Uint8Array {
const arr = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i);
}
return arr;
}
```

Create an instance of the `RequestSpecRaw` class, by supplying the target URL as the constructor.

``` ts
async function testSendRequest(sdk: SDK): Promise<void> {
console.log("Testing send request");
const req = new RequestSpecRaw("http://localhost:5555");
```
Call the `stringToUint8Array` function on the request data to convert it into a byte array. Send the request and if a response is received, print it in plaintext.
``` ts
const rawRequest = "GET /admin\x85 HTTP/1.1\r\nHost: localhost:5555\r\n\r\n";
req.setRaw(stringToUint8Array(rawRequest));

const res = await sdk.requests.send(req);
console.log(res?.response.getRaw().toText());
}
```

Since we are using a button on the frontend to issue this request, define the `testSendRequest` function as an API call and register it to the backend.

``` ts
export type API = DefineAPI<{
testSendRequest: typeof testSendRequest;
}>;

export function init(sdk: SDK<API>) {
sdk.api.register("testSendRequest", testSendRequest);
}
```

::: tip
<details>
<summary>To view the entire frontend script, expand the following:</summary>

``` ts
import type { Caido } from "@caido/sdk-frontend";
import type { API } from "../../backend/src/index";

import "./styles/index.css";

export type CaidoSDK = Caido<API>;

const Commands = {
sending: "my-plugin-page.req",
} as const;

const sending = async (sdk: CaidoSDK) => {
await sdk.backend.testSendRequest();
};

const createPage = (sdk: CaidoSDK) => {
const requestButton = sdk.ui.button({
variant: "primary",
label: "Send Request",
});

requestButton.addEventListener("click", async () => {
await sending(sdk);
});

const bodyContainer = document.createElement("div");
bodyContainer.appendChild(requestButton);

const card = sdk.ui.card({
body: bodyContainer,

});

sdk.navigation.addPage("/my-plugin-page", {
body: card,
});
};

export const init = (sdk: CaidoSDK) => {
createPage(sdk);
sdk.sidebar.registerItem("My Plugin", "/my-plugin-page", {
icon: "fas fa-rocket",
});

sdk.commands.register(Commands.sending, {
name: "Send Request",
run: () => sending(sdk),
});
};
```

</details>
6 changes: 3 additions & 3 deletions src/guides/distribution/repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The steps above will create a repository under your own account.
If you would like to host your repository under the [caido-community](https://github.com/caido-community) organization instead, you can request a repository on our [Discord server](https://links.caido.io/www-discord).
:::

## 3. Generate a key-pair
## 3. Generate a Key-Pair

Plugin packages **must** be digitally signed to be installable in Caido.

Expand Down Expand Up @@ -76,7 +76,7 @@ The file `private.pem` will contain the following format:
-----END PRIVATE KEY-----
```

### Generate the public key
### Generate the Public Key

Run the following command to generate a public key:

Expand All @@ -94,7 +94,7 @@ The file `public.pem` will contain the following format:
-----END PUBLIC KEY-----
```

## 4. Create a release
## 4. Create a Release

Now that your repository and key-pair are ready, it’s time to create a release!

Expand Down
2 changes: 1 addition & 1 deletion src/guides/distribution/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Address any required changes and update the GitHub release with the new changes.

We will publish the plugin as soon we have verified that all required changes have been addressed.

## Next steps
## What's next?

Once your plugin is published, it is time to announce it to the community ✨

Expand Down

0 comments on commit cf57780

Please sign in to comment.