· 8 years ago · Jan 20, 2018, 06:18 PM
1<?php /* Date: 1/10/2018 / * Time: 11:50 PM */ ?>
2<!DOCTYPE html>
3<html>
4<head>
5 <title>
6 SQL Injection Demo
7 </title>
8 <meta charset="utf-8" />
9</head>
10<?php
11
12
13// Define some global variables for use in this page -- if you have your own MySQL installation, which you actually
14// have to since this isn't hosted, hopefully you have changed the root password anyways -- go ahead and put in
15// whatever there or create a user for the DB if you prefer.
16DEFINE('GAME_DB', 'bobby_tables');
17DEFINE('GAME_HOST', 'localhost');
18DEFINE('GAME_USER', 'root');
19DEFINE('GAME_PW', '');
20DEFINE('GAME_PDO_DSN', 'mysql:host=' . GAME_HOST . ';dbname=' . GAME_DB);
21
22
23// // one of my favorite tidbits: print_r within <pre> makes arrays (and everything else) formatted! and visible!
24// // if you uncomment the below, it will show what's in the $_POST superglobal presently.
25/* ?> <pre><?php print_r($_POST); ?></pre><?php */
26
27function rebuild_tables()
28{
29 echo "tables rebuilt<br/>";
30
31 $conn = new PDO('mysql:host=localhost', GAME_USER, GAME_PW);
32
33 $conn->exec('create database if not exists bobby_tables; use bobby_tables; create table if not exists drop_me(id int not null auto_increment, number_col integer, text_col varchar(60), primary key (id)); insert into drop_me (number_col, text_col) values (9000, "look ma, text!");');
34
35 $conn = null;
36}
37
38//was having some issues with the equality checks and booleans, so strings :P
39function check_table_status()
40{
41 $conn = new PDO(GAME_PDO_DSN, GAME_USER, GAME_PW);
42
43 if ($conn->query('SELECT * FROM drop_me'))
44 {
45
46 $stmt = $conn->query('SELECT * FROM drop_me');
47
48 $result = $stmt->fetchAll();
49
50 $conn = null;
51
52 return "not dropped";
53 }
54 else
55 {
56 $conn = null;
57
58 return "dropped";
59 }
60}
61
62
63if (isset($_POST['rebuild_flag']))
64{
65 rebuild_tables();
66}
67
68// And this part will run if the other one has been submitted.
69if (isset($_POST['submit']))
70{
71 $difficulty = $_POST['difficulty_selection'];
72
73 $user_input = $_POST['hack_me'];
74
75 if ($difficulty == 'super_easy')
76 {
77 $conn = mysqli_connect(GAME_HOST, GAME_USER, GAME_PW, GAME_DB);
78
79 mysqli_query($conn, $user_input);
80
81 mysqli_close($conn);
82 }
83 else if ($difficulty == 'easy')
84 {
85 // The only reason I switched to PDO here is because I can't remember the mysqli syntax well enough to have
86 // gotten it to work without errors, and it's already past midnight here...!
87
88 $conn = new PDO(GAME_PDO_DSN, GAME_USER, GAME_PW);
89
90 // String interpolation lets us put the variable right into the string--which is *not* more secure. Well, it
91 // is more secure than letting completely arbitrary SQL run against your server, but...
92
93 // Many places do know enough to do mysqli_real_escape_string, which is great! but relies on human memory,
94 // which is very faulty, and relies on knowing all routes of input, escaping it all, not forgetting a single
95 // quote, etc. And goodness knows none of us have ever forgotten a quote :D
96
97 $query = "select * from drop_me where text_col = '$user_input'";
98
99 $stmt = $conn->query($query);
100
101 $result = $stmt->fetchAll();
102
103 $conn = null;
104 }
105 else if ($difficulty == 'medium')
106 {
107 $conn = new PDO(GAME_PDO_DSN, GAME_USER, GAME_PW);
108
109 /*
110 PDO's bound parametrizations utilize the :your_var_name_choice syntax. It doesn't have to match your
111 literal variable name-- `:foo` would have been equally usable below.
112
113 Do note that you *do not add single quotes* even when you are expecting a string and would use them
114 ordinarily!
115 */
116 $query = "SELECT * FROM drop_me WHERE text_col = :user_input";
117
118 $stmt = $conn->prepare($query);
119 $stmt->bindParam(':user_input', $user_input);
120 $stmt->execute();
121
122 $result = $stmt->fetchAll();
123
124 $conn = null;
125 }
126 else
127 {
128 ?>
129 <p class="error">Sorry, something has gone horrible wrong.</p>
130 <?php
131 }
132}
133
134?>
135<body>
136 <section>
137 <!--
138 If you're new to PHP, the bit below assigns the form action to be the page itself--
139 so basically, "hey, when you submit the form, send the $_POST to this page the form itself is on.
140 -->
141 <form action="<?= $_SERVER['PHP_SELF'] ?>"
142 method="post">
143
144 <h1>Drop My Tables: The Game!</h1>
145 <div>Try and drop the table
146 <code>drop_me</code>
147 (good news, you know the schema and table name! ;)) from this form!
148 </div>
149 <div>
150 <label for="difficulty_selection">
151 <b>Difficulty:</b>
152 </label>
153 <div>
154 <input type="radio"
155 name="difficulty_selection"
156 value="super_easy"
157 checked="checked">
158 Allow literal arbitrary SQL execution
159 <p>
160 <i>Go ahead an enter any SQL you want! You could even drop the database itself. (For what it's
161 worth, the rebuild tables button that will appear will recreate the database too, if
162 necessary.)
163 </i>
164 </p>
165 </div>
166 <div>
167 <input type="radio"
168 name="difficulty_selection"
169 value="easy">
170 Use a "safe" (spoiler: not) built query in the script itself
171 <p>
172 <i>
173 Something like, oh, say...
174 <code>test'; drop table drop_me;</code>
175 might be just the ticket!
176 </i>
177 </p>
178 </div>
179 <div>
180 <input type="radio"
181 name="difficulty_selection"
182 value="medium">
183 Parametrization with PDO
184 <p>
185 <i>
186 Not foolproof, but much harder. Try the ones above that work; they shouldn't here .
187 </i>
188 </p>
189 </div>
190 </div>
191 <div>
192 <label for="hack_me_id">
193 <b>Hack me:</b>
194 </label>
195
196 <input type="text"
197 id="hack_me_id"
198 name="hack_me"
199 value="<?php echo(isset($_POST['hack_me']) ? $_POST['hack_me'] : '') ?>" />
200 <br>
201 </div>
202 <input type="submit"
203 name="submit"
204 id="submit_button"
205 value="Run me!">
206
207 </form>
208 </section>
209 <section id="table_status">
210 <h2>
211 Table Status
212 </h2>
213 <p>
214 <i>
215 It's easiest to check accurately by going into your MySQL shell and checking there--for some reason,
216 it does work as expected for both the literal and quoted versions, but the quoted one while it does
217 drop the table won't show up as such on the page, but will be dropped :P Not sure why...if you see
218 my error, please let me know!
219 </i>
220 </p>
221 <?php
222
223 if (check_table_status() === "not dropped")
224 {
225 echo "Hey, the table exists! Boo. Or... yay? <br/>";
226 }
227 else
228 {
229 if (isset($_POST['submit']))
230 {
231 echo "You did it, table is gone! Yay! Wait, no...<br/>";
232 }
233 else
234 {
235 echo 'Hmm, <code>$_POST</code> is empty and the table isn\'t there...rebuild?<br/>';
236
237 }
238
239 ?>
240 <form action="<?= $_SERVER['PHP_SELF'] ?>"
241 method="post">
242 <input type="submit"
243 name="rebuild_flag"
244 value="rebuild">
245 </form>
246 <?php
247 }
248 ?>
249 </section>
250</body>
251</html>