About
Hello, I'm pwnwriter, and this is a frontend for my compilation of random notes gathered throughout the year. Here, you'll discover a diverse array of notes spanning topics such as Cybersecurity, shell scripting, rust, and much more, all available under the MIT license. These notes are straightforward, nothing but raw insights into various subjects.
Links
Website : pwnwriter.xyz
Github : @pwnwriter
Twitter : @pwnwriter
Ko-fi : @pwnwriter
Links
Allow rust-analyzer to run on single file for completion
Create a rust-project.json
in your directory
{
"sysroot_src": "path/to/the/library",
"crates": [
{
"root_module": "main.rs",
"edition": "2021",
"deps": []
}
]
}
Here, i manually defined rustup
home. By default it's ~/.rustup
export RUSTUP_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/rustup"
Define crates
deps inside a single file.
//! ```cargo //! [dependencies] //! clap = { version = "4.2", features = ["derive"] } //! ``` extern crate clap; use clap::Parser; #[derive(Parser, Debug)] #[clap(version)] struct Args { #[arg(short, long, default_value = "PwnWriter")] pub name: String, } fn main() { let args = Args::parse(); println!("{}", args.name); }
Iterators
extern crate anyhow; use anyhow::Result; fn main() -> Result<()> { let ports = vec![1, 3, 4]; let urls = vec![ "https://github.com", "https://metislinux.org", "https://kisslinux.org", ]; let urls_with_ports: Vec<String> = urls .iter() .zip(&ports) .map(|(url, &port)| format!("{}:{}", url, port)) .collect(); for url in urls_with_ports { println!("{}", url); } Ok(()) }
Standard Input Using Cursor
Sometimes, you may not have access to concatenate
a file and pipe it into your
code for testing. In such situations, you can utilize the Cursor
module.
use std::io::{Cursor, BufRead, BufReader}; fn main() { let cursor = Cursor::new("Hey\npwned\n".to_string()); let buffered_reader = BufReader::new(cursor); for line in buffered_reader.lines() { println!("{}", line.unwrap()); } }
How to Install Nix on any Linux VPS
Nix is a versatile package manager, programming language, and even a complete Linux distribution. It stands out with its focus on reproducibility and declarative configuration.
Download and Run the Installer
We'll be using determinate system's nix installer
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- --install
You should have nix installed by now.
nix --version
Let's install a pkg tmux
nix profile install nixpkgs#tmux
we can even install package for temp shell
nix-shell -p git
Installing Home Manager
Finally, we'll use Home Manager, a tool designed to manage the user environment (e.g., dotfiles) in a declarative way using Nix. We'll start by adding the minimal configuration needed to get Home Manager up and running.
First, create a dotfiles directory if you don't have one already (the name of the directory does not matter). Use git init (or your favorite GUI) to initialize a Git repository in the folder. Add the following two files:
mkdir dotfiles; cd dotfiles; git init
{
description = "Your description here";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, home-manager, ... }: {
homeConfigurations = {
<Yourname> = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [
./modules
{
home.username = "username";
home.stateVersion = "23.11";
home.homeDirectory = "/home/username";
}
];
};
};
};
}
Place the above file as flake.nix
.
Now the directory tree should look like this
This flake initiales all the modules from modules
directory.
For example to configure git
, it goes something like this
{ pkgs, ... }:
let
name = "username";
email = "main";
in
{
programs.git = {
enable = true;
userName = name;
userEmail = email;
};
}
Now we'll need to source it inside modules,
Like lua
's init.lua' we have default.nix
in nix
.
We'll now import out git.nix
using modules/default.nix
file and it goes something like this.
{ config, ... }:
let
modules = [
./git.nix
];
in
{
imports = modules;
xdg.dataHome = "${config.home.homeDirectory}/.local/share";
programs.home-manager.enable = true;
}
We can now build our config using following.
nix run github:nix-community/home-manager -- switch --flake .#your_name
It should generate your config like below.
Installing NixOS on a Hetzner VPS
In this guide, I will explain how to install NixOS on a Hetzner VPS from a local machine using flakes and Nix itself.
Requirements
- A linux os with the latest systemd, I'm using
ubuntu24.04
- A machine with Nix already installed (I have written a blog post on this topic).
- Root privileges to run commands.
That's it!
We will use nixos-anywhere
and disko
to install Nix and create declarable partitions. First, we'll import them into our flake
. The directory structure should look like this:
You'll want to change wood
to something else, i'm just imagining the host name for myvps as wolf
.
flake.nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { nixpkgs, disko, ... }: {
nixosConfigurations.wolf = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./configuration.nix
./disk-config.nix
];
};
};
}
We'll import our configuration.nix
and disk-config.nix
inside our flake. Add your public ssh keys in authorized_keys
for accessing the server after installation.
configuration.nix
{ modulesPath, config, lib, pkgs, ... }: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
(modulesPath + "/profiles/qemu-guest.nix")
./disk-config.nix
];
boot.loader.grub = {
efiSupport = true;
efiInstallAsRemovable = true;
};
services.openssh.enable = true;
environment.systemPackages = map lib.lowPrio [
pkgs.curl # define your more packages here
pkgs.gitMinimal
];
users.users.root.openssh.authorizedKeys.keys = [
"You're public ssh key"
];
system.stateVersion = "23.11";
}
Now regarding the disk, I'm using the default disk partitions required and recommended by disko
, If you happen to change you can. This disk partions are for
bios compatible gpt partition.
disk-config.nix
# Example to create a bios compatible gpt partition
{ lib, ... }:
{
disko.devices = {
disk.disk1 = {
device = lib.mkDefault "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
name = "boot";
size = "1M";
type = "EF02";
};
esp = {
name = "ESP";
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
name = "root";
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [
"defaults"
];
};
};
};
};
};
};
}
Now, we'll have to run our flake to install nix on the vps.
nix run github:nix-community/nixos-anywhere -- --flake .#wolf root@<server-ip>
It'll ask for password to install the base of nix.
And now, you should be able to log-in into the vps using the ssh keys
, regardless of the password being empty.
Setting Up Google Authentication for SSH on a Linux Server
Step 1: Install Google Authenticator
-
Update your package list:
sudo apt update
-
Install the Google Authenticator package:
sudo apt install libpam-google-authenticator
Step 2: Configure Google Authenticator for Your User
-
Run the Google Authenticator setup:
google-authenticator
-
Answer the prompts:
- "Do you want authentication tokens to be time-based (y/n)?": Type
y
and press Enter. - Backup Codes: Write down the emergency scratch codes provided and store them in a safe place.
- "Do you want me to update your "/home/username/.google_authenticator" file?": Type
y
and press Enter. - "Do you want to disallow multiple uses of the same authentication token? (y/n)": Type
y
and press Enter. - "By default, tokens are good for 30 seconds. Do you want to increase the time skew window to 4 minutes? (y/n)": Type
n
and press Enter. - "Do you want to enable rate-limiting protection? (y/n)": Type
y
and press Enter.
- "Do you want authentication tokens to be time-based (y/n)?": Type
-
Scan the QR code: Use the Google Authenticator app on your phone to scan the QR code displayed on your terminal.
Step 3: Configure SSH to Use Google Authenticator
-
Edit the SSH configuration file:
sudo vim /etc/pam.d/sshd
-
Add the following line at the end of the file:
auth required pam_google_authenticator.so nullok
-
Edit the SSH daemon configuration file:
sudo vim /etc/ssh/sshd_config
-
Find and modify the following lines:
-
Ensure
ChallengeResponseAuthentication
is set toyes
:ChallengeResponseAuthentication yes
-
Ensure
UsePAM
is set toyes
:UsePAM yes
-
(Optional) If you want to require both Google Authenticator and your password, add or modify the line:
AuthenticationMethods publickey,password publickey,keyboard-interactive
-
Step 4: Restart the SSH Service
-
Restart the SSH service to apply the changes:
sudo systemctl restart sshd
Step 5: Test the Configuration
-
Open a new SSH session to your server.
-
Log in with your username and password.
-
When prompted, enter the verification code from your Google Authenticator app.
Installing an SSL Certificate on a Domain Using Certbot manually
1. Install Certbot
If Certbot is not already installed, you can install it using the following commands:
For Debian/Ubuntu:
sudo apt update
sudo apt install certbot
2. Obtain the SSL Certificate
export DOMAIN=<your domain>
certbot certonly --manual -d *.$DOMAIN -d $DOMAIN --agree-tos --manual-public-ip-logging-ok --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory --register-unsafely-without-email --rsa-key-size 4096
3. Certbot will provide instructions on how to create a DNS TXT record to verify your domain ownership. The output will look something like this:
Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Before continuing, verify the record is deployed.
After creating the DNS TXT record, wait for the changes to propagate. This can
take a few minutes. You can verify the DNS record by using a tool like
DNSChecker
to ensure it has been properly set.
4. Setup ssl
Your new SSL certificates will be stored in the /etc/letsencrypt/live/$DOMAIN/ directory. You will find the following files:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
root /var/www/html;
index index.html index.htm;
}
}
Migrating from Jenkins to GitHub Actions for CI/CD
Introduction
Jenkins has been a go-to tool for CI/CD for many developers. However, it can be heavy and complex for smaller projects or when simplicity is desired. In this blog post, I will walk you through my experience migrating from Jenkins to GitHub Actions for CI/CD, highlighting the reasons for the switch and the steps taken to implement a new workflow.
Prerequisites
Before you start the migration, ensure you have the following:
- A GitHub repository for your project.
- Access to your VPS (Virtual Private Server) where the application will be deployed.
- SSH keys generated and configured for your VPS.
- GitHub secrets configured for storing sensitive information like SSH keys.
Why Migrate from Jenkins to GitHub Actions?
- Complexity and Maintenance: Jenkins requires managing a separate server and handling its updates and maintenance, which can be time-consuming.
- Performance: Jenkins can be resource-intensive, which might be overkill for smaller projects.
- Integration: GitHub Actions provides seamless integration with GitHub repositories, making it easier to manage workflows within the same platform.
- Simplicity: GitHub Actions offers a straightforward YAML configuration, which is easier to read and manage compared to Jenkins' XML-based configurations.
being small but absolute working workaround works as follows.
- This workflow run on push on
main
branch and deploys in github and then only in vps.
In this guide, we'll be using appleboy/ssh-action, which lets you ssh into the server and run particular script.
For this workflow, we'll first need to have our ssh keys in our vps and the user must be able to login via ssh.
You'll want to generate ssh keys and then put your public keys in authorized_keys
, which simply lets the current user ssh into the sever.
Setting Up the GitHub Actions Workflow
After generating ssh keys, put your:
- PRIVATE_KEY, USERNAME, and HOST in your repository's secrets variables.
Here’s the GitHub Actions workflow that I set up to build and deploy my web application to a VPS:
name: Test the web app and deploy on vps
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Nix package manager
uses: DeterminateSystems/nix-installer-action@main
- name: Install Node.js and pnpm
run: |
nix profile install nixpkgs#nodejs_22
nix profile install nixpkgs#nodePackages_latest.pnpm
- name: Build website
run: |
pnpm install
pnpm run build
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy over the vps
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.PRIVATE_KEY }}
port: 22
script: |
cd ~/source_dir/
bash ./.github/deployer.sh
A graph of this workflow
The deployer script
The following script is ran by github actions on github build success
#!/usr/bin/env bash
# Written by @pwnwriter
# This script builds the Nest Nepal website. If the build fails, it attempts to build from the previous commit.
### Variables
USER="ubuntu"
REPO_DIR="/home/${USER}/repo_name"
INFO_DIR="/home/${USER}/repo_name-log"
LOG_FILE="$INFO_DIR/build.log"
SERVER_LOG="$INFO_DIR/server.log"
PORT=3002
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# -------------------- Helper functions ---------
# Appends log messages
append_log() {
printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE"
}
# Prints informational messages
print_info() {
local message="${1}"
printf "${YELLOW}[INFO]${NC} %s\n" "$message"
append_log "[INFO] $message"
}
# Prints success messages
print_success() {
local message="${1}"
printf "${GREEN}[SUCCESS]${NC} %s\n" "$message"
append_log "[SUCCESS] $message"
}
# Prints error messages
print_error() {
local message="${1}"
printf "${RED}[ERROR]${NC} %s\n" "$message"
append_log "[ERROR] $message"
}
# Creates log directories
create_log_dirs() {
print_info "Creating log directories"
mkdir -p "$INFO_DIR" || {
print_error "Failed to create log directory"
exit 1
}
}
# Kills the current running process $port
kill_existing() {
local pid
pid=$(ss -lptn "sport = :$PORT" | grep -oP '(?<=pid=)\d+')
if [ -n "$pid" ]; then
print_info "Killing existing process with PID $pid"
kill -9 "$pid" || {
print_error "Failed to kill process with PID $pid"
exit 1
}
else
print_info "No existing process found on port $PORT"
fi
}
# Builds the website
website_build() {
print_info "Installing required modules"
pnpm install 2>&1 | tee -a "$LOG_FILE" || {
print_error "Failed to install modules"
return 1
}
print_info "Building website"
pnpm run build 2>&1 | tee -a "$LOG_FILE"
if [ $? -eq 0 ]; then
print_success "Build successful, starting website"
kill_existing
nohup pnpm start >>"$SERVER_LOG" 2>&1 &
print_success "Website is live and running"
else
print_error "Build failed"
return 1
fi
}
# Main function
main() {
cd "$REPO_DIR" || {
print_error "Unable to change directory to $REPO_DIR"
exit 1
}
print_info "Pulling latest changes from the main branch"
git pull origin main --rebase || {
print_error "Failed to pull latest changes from main branch"
exit 1
}
create_log_dirs
website_build
if [ $? -ne 0 ]; then
print_info "Build failed, attempting to build from previous commit"
print_info "Reverting to previous commit"
git checkout HEAD~ || {
print_error "Failed to revert to previous commit"
exit 1
}
website_build || {
print_error "Build from previous commit failed"
exit 1
}
fi
}
# Execute
main
Mount a directory on RAM.
Mount a directory completely on RAM. Everything will be wiped out on reboot.
mount -o size=<x>G -t tmpfs none <dir_name>
Explanation
size=<x>G
: Sets the directory size in RAM (replace
-t tmpfs
: Specifies tmpfs as the RAM-based filesystem.
none
: Represents the virtual directory in RAM, not tied to any physical device.
<dir_name>
: Path to the directory where you'll access the RAM directory.
Create SSH Keys
ssh-keygen -t rsa -b 4096 -C "example@example.com"
Generate a new SSH key using RSA encryption with a bit size of 4096 and attach an email address as a label.
Start SSH Agent
eval $(ssh-agent -s)
initialize ssh keys.
Add Generated Key to SSH Agent
ssh-add "~/.ssh/ssh_file_name"
Add newly generated SSH key to the SSH agent.
Some useful commands for enumerating *nix os
Commands | Explanation |
---|---|
uname -a | Print all available system information |
uname -r | Kernel release |
uname -n | System hostname |
hostname | As above |
uname -m | Linux kernel architecture (32 or 64 bit) |
cat /proc/version | Kernel information |
cat /etc/*-release | Distribution information |
cat /etc/issue | As above |
cat /proc/cpuinfo | CPU information |
df -a | File system information |
Users & Groups:
Commands | Explanation |
---|---|
cat /etc/passwd | List all users on the system |
cat /etc/group | List all groups on the system |
cat /etc/shadow | Show user hashes – Privileged command |
grep -v -E "^#" /etc/passwd | awk -F: '$3 == 0 { print $1} | List all super user accounts |
finger | Users currently logged in |
pinky | As above |
users | As above |
who -a | As above |
w | Who is currently logged in and what they’re doing |
last | Listing of last logged on users |
lastlog | Information on when all users last logged in |
lastlog –u %username% | Information on when the specified user last logged in |
User & Privilege Information:
Commands | Explanation |
---|---|
whoami | Current username |
id | Current user information |
cat /etc/sudoers | Who’s allowed to do what as root – Privileged command |
sudo -l | Can the current user perform anything as root |
Environmental Information:
Commands | Explanation |
---|---|
env | Display environmental variables |
set | As above |
echo $PATH | Path information |
history | Displays command history of current user |
pwd | Print working directory, i.e. ‘where am I’ |
cat /etc/profile | Display default system variables |
Interesting Files:
Commands | Explanation |
---|---|
find / -perm -4000 -type f 2>/dev/null | Find SUID files |
find / -uid 0 -perm -4000 -type f 2>/dev/null | Find SUID files owned by root |
Common Shell Escape Sequences:
Commands | Explanation |
---|---|
:!bash | vi, vim |
:set shell=/bin/bash :shell | vi, vim |
!bash | man, more, less |
find / -exec /usr/bin/awk 'BEGIN {system("/bin/bash")}' \; | find |
awk 'BEGIN {system("/bin/bash")}' | awk |
--interactive | nmap |
perl -e 'exec "/bin/bash";' | Perl |
Random scripts
Record with internal sound using wf-recorder
wf-recorder \
-aalsa_input.pci-0000_03_00.6.analog-stereo \
-aalsa_output.pci-0000_03_00.6.analog-stereo.monitor \
-f recording.mkv