0%

密码出题指北

密码出题指北

前言

换届了,🐬学长退休了😭😭😭

时间过的真快,数个月前(总之不到一年),21级的我们还是以新生的姿态蛰伏在实验室

👀眨眼间,就该变成实验室CTF主力了

想来,得给将至未至的新生出些许密码题目

但只是静态的py脚本,可真让人觉得枯燥🤔

遂于六月一日晚十一时,尝试琢磨学习,密码如何出交互式的题目

希望能对以后将成主力的学弟学妹们,有一些帮助

主要参考

配置

M1 MacBook Pro

Docker for mac

(或许win才是主流,说到底配起来也大同小异。而win可以参考的可太多了,不差这一篇)

跟着👆链接走

  1. 安装Docker

  2. 阿里源,需要注册账号先(这一步可忽略)

    https://tdll0jog.mirror.aliyuncs.com)

    step1

    step2

  3. 注册Docker iD,登陆

    这里实测,发现用了第2步的阿里,就登陆不上了

    索性先不换源了,把代理关了

    (若你执意要换,且登陆失败,可以参考这个)

docker 基本使用

docker命令
  • docker

    进入终端,输入docker回车,可查看docker支持的命令

  • docker search nginx

    搜索nginx镜像

  • docker pull nginx:xxx 获取指定版本的nginx镜像

    docker pull nginx 获取最新版本的nginx镜像

  • 创建并启动容器(可以使用http://localhost:8080/访问docker容器)

    1
    2
    3
    4
    # -d 后台运行
    # -p 8080:80 宿主机的8080端口映射到docker内部的80端口
    # --name docker-nginx 启动后的容器名称为docker-nginx
    docker run -d -p 8080:80 --name docker-nginx nginx
  • 查看容器

    1
    2
    3
    4
    5
    # 查看运行中的容器
    docker ps

    # 查看所有容器 包括正在运行和已经停止运行的
    docker ps -a
  • 停止容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 通过id直接关闭容器
    # docker kill a0fbf4519279
    # 通过容器名称直接关闭容器
    docker kill docker-nginx


    # 通过id直接容器 默认等待十秒 超时强制关闭
    # docker stop a0fbf4519279
    # 通过容器名称关闭容器 默认等待十秒 超时强制关闭 等价于 docker stop -t=10 docker-nginx
    docker stop docker-nginx
  • 启动停止的容器

    1
    2
    3
    4
    5
    6
    # 启动容器可通过容器id或者容器名称
    # 通过容器名称启动容器,如果已启动则忽略
    docker start docker-nginx

    # 通过容器名称重新启动容器,如果未启动则直接启动,如果已启动则关闭再启动
    # docker restart docker-nginx

开启一道CTF密码题

跟着4XWill师傅走

文件配置

以一道极其简单的题目为例

file

新建文件夹,并将以下文件放入其中

Dockerfile (一点都不能有出入!!!)

1
2
3
4
5
6
7
8
9
10
11
FROM python:3.8
LABEL Description="baby_try" VERSION='1.0'

COPY server.py .
COPY secret.py .

RUN chmod +x server.py

EXPOSE 12345

CMD ["python", "server.py"]

exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib
from string import ascii_letters, digits
from pwn import *
from itertools import product

table = ascii_letters + digits


class Solve():
def __init__(self):
self.sh = remote('127.0.0.1', 12345)
# self.sh = remote('121.36.197.254', 9999)

def proof_of_work(self):
# [+] sha256(XXXX+JaakUDSfxkW0xjzV) == 4dbfdc61cb88f5bd08d87493ac62e5ab174780f5f019051f91df8b3c36564ed0
# [+] Plz tell me XXXX:
proof = self.sh.recvuntil(b'[+] Plz tell me XXXX:')
tail = proof[16:32].decode()
_hash = proof[37:101].decode()
for i in product(table, repeat=4):
head = ''.join(i)
t = hashlib.sha256((head + tail).encode()).hexdigest()
if t == _hash:
self.sh.sendline(head.encode())
break

def solve(self):
self.proof_of_work()
self.sh.recvline()
flag = self.sh.recvline()[:-1].decode()
print(flag)
self.sh.close()


if __name__ == '__main__':
solution = Solve()
solution.solve()

secret.py

1
flag = b"flag{JUST_THE_FIRST_TEST_OF_CRYPTO}"

server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from hashlib import sha256
import socketserver
from secret import flag
import signal
import string
import random
import os


class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()

def send(self, msg, newline=True):
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass

def recv(self, prompt=b'[-] '):
self.send(prompt, newline=False)
return self._recvall()

def proof_of_work(self):
random.seed(os.urandom(8))
proof = ''.join(
[random.choice(string.ascii_letters+string.digits) for _ in range(20)])
_hexdigest = sha256(proof.encode()).hexdigest()
self.send(f"[+] sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
return False
return True

def handle(self):
signal.alarm(60)
if not self.proof_of_work():
self.send(b'[!] Wrong!')
return

self.send(b'here is your flag')
self.send(flag)


class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass


if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 10001
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
print(HOST, PORT)
server.serve_forever()

cd 到此文件夹

创建镜像

1
$ docker build . -t baby_try

常见报错处理

创建容器

1
$ docker run --name trytry -d -idt -p 12345:10001 baby_try

把容器映射到本地12345端口,即:127.0.0.1:12345

测试

本地连接

nc 127.0.0.1 12345

test

ohhhhhhhhhh! 芜湖 🛫️

这半夜十二点让我猛一精神,身心愉悦!

exp打入

在pycharm里把exp.py跑起来(终端我没配置好,故此)

exp

打通了,忙活了一个小时十七分钟。

停止容器

  • docker stop [CONTAINER ID] or docker stop [name]

    不知道ID 或 名字, 可以用docker ps -a来查看!

明天再看看怎么买个服务器并架在上面吧!

开心😄,安然入梦。

6.2中午两点

做完核酸了,赶紧跑来实验室捣鼓👀

开容器就给我犯了难,仔细一看用run的指令,是来创建

而显然,昨天的那个我们没有删除只是暂停了,而对此我们需要开启

开启容器

重新开机记得先把docker打开

docker

再于终端输入指令

docker start [CONTAINER ID] or docker start [name]

其实文章前半部分已经有总结了,还是需要多试试才熟悉🤔

挂载到服务器

这里我使用已经注册过账号的阿里云(阿里,打钱!👿)

其旗下的云翼计划,似乎针对学生有优惠

我用的是它的试用一个月🤤🤤🤤(反正目前是白嫖的

4XWi11师傅参考的链接里大师傅写的教程,是CentOS

但前面选服务器配置的时候看到Ubuntu时不自觉的点击了🤧

(实验室有其他师傅比较了解这个,想着是linux就…

那么,就摸黑前行

Docker 安装

找到服务器的指令端,按照菜鸟教程,键入并执行

指令端

curl -sSL https://get.daocloud.io/docker | sh

成功后,输入docker验证一下

验证

接下来的思路,跟上文已经指出的本地安装一致

服务器配置

  1. apt-get install git -y

    安装git

  2. git clone ....

    上传题目到github仓库,让服务器端借此下载文件

    (这一步不固定,总之就是让云服务器上有你本地的题目文件夹里的内容)

  3. 网页端的终端执行以下代码,就都看到了!

    1
    2
    3
    4
    #!/bin/bash
    cd CryptoTest
    ls
    cat exp.py
    (学到了,要把指令同时执行,不然执行完一次cd后,会自动cd回初始目录,白搭👿)
    
    ![芜湖](https://cdn.jsdelivr.net/gh/Harry0597/Imag@master/uPic/image-20220602184456699.png)
  4. 跟着本地的教程再走一遍

    • 建立镜像

      1
      2
      cd CryptoTest
      docker build . -t test1
    • 开放2022端口

      1
      2
      3
        #!/bin/bash
      cd CryptoTest
      docker run --name try1 -d -idt -p 2022:10001 test1
      • 本地测试能否连接服务器端端口

        没反应🤔

        sudo ufw allow 2022 尝试开放2022端口

        1
        2
        3
        #!/bin/bash
        cd CryptoTest
        docker run --name try -d -p 12345:10001 test1
ssh登陆

除了在阿里云官网的控制台发送指令,mac终端也可连接服务器

ssh root@服务器公网IP

直接登陆好吧!

失败乃成功之母

以下为当时的摸索(大多无直接效益)

但确实让我对linux等熟悉了些

socat

企图放弃docker,直接用socat对外暴露端口


离谱,脑子糊涂了CentOS需要用到yum 来下载socat,我下了半天没下到yum

因为Ubuntu是用apt-get install socat里apt-get下的啊🤧

nohup socat -d -d tcp-l:2022,reuseaddr,fork EXEC:"python -u server.py"

socat tcp-listen:2022,fork exec:"server",reuseaddr

??? 失败,本地nc照样连不上


以为是防火墙有问题🤨

此时 root账户 哪怕我退而求其次,在本地里也不能正常用此docker了

Error response from daemon: driver failed programming external connectivity on endpoint XXX(端口映射或启动容器时报错)

原因在我们启动了Docker后,我们再对防火墙firewalld进行操作,就会发生上述报错

systemctl restart docker

重启docker服务及可重新生成自定义链DOCKER

如此,还是没对外开放


阿里云开放端口

image-20220602231141650

image-20220602231213063

image-20220602231441172

手动添加12345端口即可,发现可以本地nc连接上端口了

Linux 基础指令

「最短的捷径就是绕远」

https://blog.csdn.net/zengmingen/article/details/50802555

调试、测试过程,意外的学习收获。

来实验室之前,零散的知道些

现在有稍微全一点的,还有使用环境,真是太棒了😃

docker 重启后,又连接不了了

已知要求

  1. 阿里云开放端口
  2. ······

发现了,因为重启了,需要再docker start name🤧

备用选项

  • firewall-cmd --add-port=12345/tcp --permanent
    firewall-cmd --zone=public --add-port=12345/tcp --permanent

    开放2022端口

  • sudo ufw allow 2022

    尝试开放2022端口

改天再开放一个端口,控制变量就知道了哪些是关键步骤了

总而言之,捣鼓了一天

nc 39.105.3.122 12345,成功(欢迎解题exp打入😈)

下一步该想,如何实现动态docker端口发放(目前觉得需要网页端配合,不是单纯服务器+docker的事)

6月2日23:57


修改代码等内容后,没反应

6月3日早九点

难办,我更换了flag后,nc上去exp打入得到的却还是老flag

🤔重启容器无效,难到要暂时关闭端口再开放?

我悟!

流程中,是先建立了image(镜像),然后根据镜像建立的容器,无论我怎么重启容器,它的内容都是跟建立镜像时一致

故此,修改内容后,应该再build一个新的镜像,再以此建立新容器

  • 修改好题目文件的内容(Linux指令复习)
    • vim file
    • 输入i或·e,进入编辑模式
    • 修改完后,按esc退出编辑
    • 先输入:,后输入wq,保存修改退出
  • 查看所有镜像 docker images
  • 删除镜像docker rmi [image name]
  • 建立镜像docker build . -t [image name]
  • 创建容器docker run --name [container name] -d -idt -p [port]:10001 [image name]

至此,取得阶段性成果,也编写好markdown文件了

6.3 23:34

(密码好难🤯)