{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Hotdog (not elegant)\n\nThis is the first challenge in the AI Village CTF @DEFCON. \n\nThe objective of this challenge is foolding a classifier, we want our beloved Chester to be classified as an Hot-Dog.\n\nFirst thing, let's say Hi to Chester.","metadata":{}},{"cell_type":"code","source":"from IPython.display import Image as Ima\nIma(filename=f'/kaggle/input/ai-village-ctf/hotdog/chester.png')","metadata":{"execution":{"iopub.status.busy":"2022-09-09T23:43:33.164217Z","iopub.execute_input":"2022-09-09T23:43:33.164878Z","iopub.status.idle":"2022-09-09T23:43:33.198532Z","shell.execute_reply.started":"2022-09-09T23:43:33.164818Z","shell.execute_reply":"2022-09-09T23:43:33.197129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In this notebook I present my solution to this problem, we can solve this challenge by randomly transforming the image so that the resulting image confuses the neural network, in particular i consider adding gaussian noise and randomly rotating the image.\nThis is a blackbox attack that have no assumptions on the model used in the backend. For this reason it requires querying the model several times.\n\nActually there is a trivial solution that I just write here but I don't use it in the notebook.\nThe solutin is replacing the image of chester with an image of a hotdog, or for a bit more elegant solution average the pixel values for an hotdog image and chester image (giving more weight to the hotdog if the classifier still sees chester and not the hotdog).\n","metadata":{}},{"cell_type":"code","source":"\nimport pandas as pd\nimport numpy as np\nimport os\nimport requests\nfrom IPython.display import clear_output, display\nfrom PIL import Image as Im\nfrom torch.utils.data import Dataset, DataLoader\nimport torch\nimport joblib\nimport matplotlib.pyplot as plt\nimport json\nimport base64\nimport shutil\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n\ndir_name = \"/kaggle/input/ai-village-ctf\"\n\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-09-09T23:43:33.201352Z","iopub.execute_input":"2022-09-09T23:43:33.202485Z","iopub.status.idle":"2022-09-09T23:43:35.369388Z","shell.execute_reply.started":"2022-09-09T23:43:33.202426Z","shell.execute_reply":"2022-09-09T23:43:35.367627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def add_flag(challenge_id: str, flag: str) -> str:\n    try:\n        submission_path = \"/kaggle/working/submission.csv\"\n        template_path = f\"{dir_name}/submission.csv\"\n        if not os.path.isfile(submission_path) :\n            shutil.copyfile(template_path, submission_path)\n        flags = pd.read_csv(submission_path, index_col=\"challenge_id\")\n        valid = flags.index.to_list()\n        if challenge_id in valid:\n            flags.at[challenge_id] = flag\n            flags.to_csv(submission_path)\n            print(\"Flag saved. If you want to update the scoreboard, click 'submit' on the right. Remember that you can only update your score on the scoreboard 50 times per day\")\n        else:\n            print(\"Invalid challenge_id\")\n    except:\n        print(\"We had trouble saving that flag. Keep a copy of it and contact the CTF organizers.\")","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-09-09T23:43:35.371901Z","iopub.execute_input":"2022-09-09T23:43:35.372839Z","iopub.status.idle":"2022-09-09T23:43:35.392025Z","shell.execute_reply.started":"2022-09-09T23:43:35.372769Z","shell.execute_reply":"2022-09-09T23:43:35.387286Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# HOTDOG (id: hotdog) -- 200 Points","metadata":{}},{"cell_type":"code","source":"IMAGE_DIMS = (224, 224)\nimport time \ndef noisy_image(seed,rotate=True):\n    #seed for reproducibility\n    np.random.seed(10500+seed)\n    \n    img = Im.open(f\"{dir_name}/hotdog/chester.png\")\n    if rotate:\n        #randomly rotate the image\n        img = img.resize(IMAGE_DIMS).rotate(np.random.randint(0,360))\n    else:\n        img = img.resize(IMAGE_DIMS)\n    #convert to numpy\n    pix = np.array(img)\n    #add noise and clip to valid rgb range (0-255)\n    pix = pix + np.random.normal(0,1,pix.shape)\n    pix = np.clip(pix,a_min=0,a_max=255).astype('uint8')\n    \n    #convert from numpy to PIL Image and submit\n    img = Im.fromarray(pix,'RGB')\n    img_bytes = img.tobytes()\n    \n    b64_img_bytes = base64.urlsafe_b64encode(img_bytes)\n\n    try:\n        time.sleep(0.1)\n        r = requests.post(\"https://hotdog.fly.dev/score\", data=json.dumps({\"input\": b64_img_bytes.decode()}))\n    except requests.exceptions.ConnectionError:\n        print(\"Connection problems. Contact the CTF organizers.\")\n    return r.text,img","metadata":{"execution":{"iopub.status.busy":"2022-09-09T23:51:18.829800Z","iopub.execute_input":"2022-09-09T23:51:18.830422Z","iopub.status.idle":"2022-09-09T23:51:18.842859Z","shell.execute_reply.started":"2022-09-09T23:51:18.830374Z","shell.execute_reply":"2022-09-09T23:51:18.841194Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(100000):\n    #generate noisy image , submit it and get message from classifier\n    out,_=noisy_image(i)\n    #if message is in the response we got our flag\n    if \"message\" in out:\n        break","metadata":{"execution":{"iopub.status.busy":"2022-09-09T23:51:20.112878Z","iopub.execute_input":"2022-09-09T23:51:20.113415Z","iopub.status.idle":"2022-09-09T23:52:30.521042Z","shell.execute_reply.started":"2022-09-09T23:51:20.113374Z","shell.execute_reply":"2022-09-09T23:52:30.519503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Now we will meet our favourite hotdog, let's see if Chester is feeling well","metadata":{}},{"cell_type":"code","source":"out,img=noisy_image(i)\nimg.save(\"hotdog.png\")\nimg","metadata":{"execution":{"iopub.status.busy":"2022-09-09T23:52:30.523480Z","iopub.execute_input":"2022-09-09T23:52:30.524039Z","iopub.status.idle":"2022-09-09T23:52:31.068374Z","shell.execute_reply.started":"2022-09-09T23:52:30.523984Z","shell.execute_reply":"2022-09-09T23:52:31.066922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chester seems to feel quite rotated, hopefully in hotterdog Chester will feel better and be more himself.\nBut we all know deep inside that he will be our favourite hotdog.","metadata":{}},{"cell_type":"code","source":"#submit flag\nadd_flag(\"hotdog\",out.split(\"message\\\": \\\"\")[1].split(\"\\\"\")[0])\nout.split(\"message\\\": \\\"\")[1].split(\"\\\"\")[0]","metadata":{"execution":{"iopub.status.busy":"2022-09-09T23:52:31.071051Z","iopub.execute_input":"2022-09-09T23:52:31.071510Z","iopub.status.idle":"2022-09-09T23:52:31.088971Z","shell.execute_reply.started":"2022-09-09T23:52:31.071472Z","shell.execute_reply":"2022-09-09T23:52:31.086960Z"},"trusted":true},"execution_count":null,"outputs":[]}]}