Twenty One Zero-Days in FFmpeg
Posted by redbell 4 days ago
Comments
Comment by zerobees 4 days ago
https://security.googleblog.com/2014/01/ffmpeg-and-thousand-...
So, while it's a demo of the capabilities of LLMs, this should not be at all surprising. Ffmpeg is absolutely not something you should be running outside of a sandbox if you're touching any untrusted or user-supplied content. I know that people do, and these people are taking unreasonable risks.
Comment by simjnd 4 days ago
Comment by LegionMammal978 3 days ago
Now, personally, when I file a bug report for a FOSS project I like to suggest an underlying cause and fix if I can figure it out, but I more rarely just submit a PR outright.
Comment by ftchd 3 days ago
Comment by sph 3 days ago
Comment by xboxnolifes 3 days ago
Comment by j1elo 3 days ago
I felt this is kinda like there being a large number of people willing to send spam email, but a comparatively minuscule number of people willing to work on ML filters to block it.
Comment by simjnd 2 days ago
In most cases researchers have no interest in actually "making the software better" and publishing vulns is just a way to increase their cred to land a better job.
FFMPEG's position as a well know very popular open source project means it's very interesting for this type of researcher to find a vuln and put their name on it.
It's an exhausting dynamic.
Comment by codedokode 4 days ago
Comment by ivanjermakov 4 days ago
Comment by IshKebab 3 days ago
Comment by bybrooklyn 1 day ago
Comment by leonidasrup 3 days ago
Comment by cryo32 4 days ago
99% of what I throw though ffmpeg is trusted i.e. I created it. It’s not a major risk.
Comment by acdha 3 days ago
In contrast, ffmpeg is exactly the sweet spot for a memory-safe language with those complex decoders operating on data which is often untrusted. I wouldn’t suggest a project of that scale lightly but it’s at least a near-perfect fit on the problem domain.
Comment by krageon 3 days ago
Comment by anoneng 3 days ago
With ffmpeg, anyone who knows anything about secure application development in the past 20 years knows that it is a huge security tarpit and throwing it untrusted inputs in trusted environment is asking to be owned. You thoroughly sandbox that shit. That’s true for all untrusted media conversion, but absolutely with ffmpeg.
Comment by zahlman 3 days ago
True.
That doesn't make them "very exposed to memory safety errors".
Comment by acdha 3 days ago
Comment by IshKebab 3 days ago
Also security isn't the only reason to prefer Rust to C.
But I do agree ffmpeg would see a much bigger benefit from being written in Rust.
Comment by acdha 3 days ago
What could help would be a modern API implementing the same patterns that GNU coreutils evolved over the last 4 decades but that’d be less the language than the library and it’d only go so far because some of those utilities legitimately need to things which are otherwise rare in most applications.
Comment by codedokode 3 days ago
Comment by uecker 3 days ago
Comment by shermantanktop 3 days ago
But we’re combining probability of error creation (which is effectively constant) and the limits of human cognition.
Some things are impossible at one scale, become possible at another, and become inevitable at yet another.
Comment by codedokode 3 days ago
Comment by uecker 3 days ago
There is also the question whether trading memory safety against supply chain risks is really worth it.
Comment by jstanley 4 days ago
They were talking about how there was a vulnerability in an extremely niche codec that is only used for one video game from the 90s or something, and were saying that the person who reported the vulnerability was acting like it was a big deal but it's really not because this codec is hardly ever used.
I was left wondering whether they were oblivious to the fact that an attacker who can supply a video file to you is free to use whatever video codec they want? It wouldn't matter if the developers thought the codec was never used at all; if it is still available then an attacker can use it.
Or was I just missing something? Is there a good reason why vulnerabilities in this codec are not a big deal after all?
Comment by m4rtink 4 days ago
Comment by TD-Linux 3 days ago
Comment by franga2000 4 days ago
If your service works by taking whatever file the user gives you and shoving it into unsandboxed ffmpeg, you've already fucked up. It would be nice if you could do that, but that's not a guarantee ffmpeg has ever provided, nor would it make sense for them to spend their limited resources on it.
Comment by RetroTechie 3 days ago
Isn't that what fuzzing and input validation is about? Most bugs presented in article suggest failures in the latter.
Comment by defrost 4 days ago
Comment by twobitshifter 3 days ago
Comment by endofreach 4 days ago
Comment by dspillett 3 days ago
IIRC that is currently sandboxed ffmpeg.
Until the people going on about making an equivalent in pure rust being automatically much safer, stop talking about it and actually get on with making it, of course!
Comment by preg_match 3 days ago
It’s not just FFmpeg. Apple has had more vulnerabilities in image and video decoders than I can count. That stuff is just very hard, and FFmpeg is doing more than anyone else.
Comment by nulltype 3 days ago
Comment by teravor 4 days ago
Comment by BoingBoomTschak 3 days ago
Personally, I still try to contain them a bit: https://git.sr.ht/~q3cpma/ezbwrap/tree/master/item/profiles
Comment by loeg 4 days ago
Comment by dspillett 3 days ago
Comment by hsbauauvhabzb 3 days ago
Comment by insanitybit 4 days ago
Security is the punch line for ffmpeg.
Comment by grahamjperrin 4 days ago
Comment by KPGv2 4 days ago
goddamn, and this is a project that prides itself on having had-written assembly in it
Comment by breppp 4 days ago
Comment by rcbdev 4 days ago
Comment by hootz 4 days ago
Comment by stackghost 4 days ago
Comment by KPGv2 4 days ago
Comment by hdgvhicv 4 days ago
I couldn’t believe they had fallen for an April fools so hard.
Comment by lkt 4 days ago
I agree it reflects poorly on them though
Comment by grahamjperrin 4 days ago
Do you have an example?
Comment by lukaslalinsky 4 days ago
Comment by krageon 3 days ago
Comment by naturalmovement 4 days ago
Comment by duped 4 days ago
That said, that dude has a point. "Researchers" chasing clout with their names attached to CVEs is kind of ridiculous. Half these CVEs are missing bounds checks that can be fixed with a patch in as much effort as writing up the blog post announcing that there was a missing bounds check.
Comment by boomlinde 4 days ago
Comment by duped 3 days ago
Comment by boomlinde 2 days ago
Comment by naturalmovement 4 days ago
ffmpeg is Free Software. You are also free not to use it.
Oddly enough, despite all these endless grievances, no one has come up with a better or more capable tool, certainly not one that is freely available.
Evidently no one cares either, because most implementations of ffmpeg I've seen typically run it as root "because we have to". Don't worry we use Docker bro.
Comment by LeoPanthera 4 days ago
Comment by bawolff 4 days ago
Actual well written vulnerability reports are not the same as slop.
AI slop is a real problem and annoying. Just because it exists does not mean every vulnerability report is AI slop.
Ffmpeg devs are free not to care, but then they cant complain when they start to get a bad reputation.
Comment by krageon 3 days ago
The advent of LLMs has made this a hundred times worse. Both because it makes it easier for most people to create reports that sound good (and so are more effort to dissect) and because people who didn't have to work hard to get any amount of competence are usually more entitled and more rude (the stakes are even lower for them).
It is economically no longer a good idea to run a bug bounty program at all. I honestly question whether or not even having a direct input for such things makes any sense anymore. The volume is becoming so great you need a classical spam filter to plow through it. But that won't work, because they all sound reasonable.
Comment by naturalmovement 4 days ago
Ok but who is going to sift through it all to triage the good bits when you're working on something for free?
> Ffmpeg devs are free not to care, but then they cant complain when they start to get a bad reputation
Who gives a shit about reputation when you're the only game in town?
There is nothing out there that even attempts to approximate an ffmpeg clone. They are the Swiss army knife of media encoding and all complainers have produced are plastic sporks.
Comment by notenlish 3 days ago
Comment by bawolff 4 days ago
Its like anything else in open source. Maintainers will do so if they care. Maybe they decide they don't care. That is always their decision to make but there are consequences for the project. Maybe those consequences make sense. Being a maintainer is all about making cost-benefit trade offs.
> Who gives a shit about reputation when you're the only game in town?
Its up to the maintainers whether they care or not. It depends on what they value.
Ultimately if maintainers make decisions that are at odds with what their userbase want, someone eventually forks and people vote with their feet.
Comment by naturalmovement 4 days ago
Today it's an industry driven by unscrupulous clout-chasers and a commitment to quantity over quality.
There is a difference between going through patches and pull requests vs. the endless stream of LLM-assisted bullshit that has started cluttering security inboxes in the last few years.
Comment by tptacek 4 days ago
Comment by dspillett 3 days ago
Comment by tptacek 3 days ago
Comment by dspillett 3 days ago
Yes there is, because:
> The vulnerability is either real or it isn't.
this, exactly: sometimes the vulnerability isn't, or isn't a fraction as serious as it is made out to be because it doesn't affect any sane configuration. And the project contributors don't know this until they've wasted time looking into it, time that could be spent looking into actual serious problems.
The extra problem right now is several people/groups dropping the same set of vulnerabilities with not coordination because they've got this great new tool to garner attention and want to be first. So projects have several things to look into that turn out to be exactly the same thing.
Comment by tptacek 3 days ago
Comment by akerl_ 3 days ago
Comment by dspillett 2 days ago
--------
[1] for example, see comments elsewhere in this thread saying things like "maintainers will if they care"
Comment by akerl_ 2 days ago
It’s up to each of us to decide which peoples’ opinions we actually care about.
Comment by dspillett 3 days ago
Caring is only part of the problem. If you are inundated by low quality reports, or many duplicates of what turn out to effectively be the same problem, that you have to sift through to find the useful reports, then by the time you have something actionable you have no time left to take action on it.
The amount of reports coming in, particularly the low/zero quality ones, is apparently growing at a much faster rate than the time volunteers have for dealing with them.
Caring does not magically solve problems without enough people with enough time.
Comment by RossBencina 3 days ago
Comment by eipi10_hn 4 days ago
Comment by bawolff 4 days ago
Until someone cares enough to do it. This is open source software. When it comes to open source, the golden rule is you either do the things you care about yourself or stfu.
Given the libav fork wasn't all that long ago, it can obviously happen to ffmpeg just as much as it can happen to any other project.
Comment by oinoom 4 days ago
Comment by mjg59 4 days ago
Comment by wavemode 4 days ago
Comment by pibaker 4 days ago
Comment by plaguuuuuu 4 days ago
Comment by z0ltan 3 days ago
Comment by zerobees 4 days ago
The developers of ffmpeg are very good at the first thing and not very good at the second. But few people on this planet, if instructed to write a complex video format parser in C or assembly, can produce something that's secure on the first try. The main failing of the ffmpeg team is that they should have spent more time on architectural hardening and mitigations. Most other large projects of this type do.
Comment by HappMacDonald 4 days ago
Comment by mr_mitm 4 days ago
Comment by RossBencina 3 days ago
Comment by endofreach 4 days ago
Except yourself, presumably, to me it almost seems nobody is perfect.
Comment by pibaker 4 days ago
Comment by bravoetch 4 days ago
Comment by mr_toad 3 days ago
Comment by hahn-kev 3 days ago
Comment by nerdsniper 4 days ago
Comment by derf_ 4 days ago
In both cases you are best off restricting things to what you actually use.
Comment by samiv 4 days ago
Within the framework there are multitudes of plugin packages that contain said elements and many of them are built on top of ffmpeg.
Comment by WD-42 4 days ago
Different cases really I think both are good.
Comment by hackernudes 4 days ago
Gstreamer has a different model, chaining together plugins. Lots of overlap, but I think Gstreamer only has real traction because some silicon vendors use it.
Comment by hugmynutus 4 days ago
ffmpeg's core functionality (encode, decode, streams, pipes, channels) are all implemented in `libav` which gstreamer links against.
Comment by harrall 4 days ago
ffmpeg and other media frameworks (Windows Media Foundation, Apple’s AVFramwork) only support static pipelines. You can use “switcher” components but the inputs are still static.
GStreamer is extremely special. The only thing that comes close was Microsoft’s DirectShow, which has since been replaced with Media Foundation which can’t do it. And while DirectShow did support it, it was fragile because many 3rd party filters did not support dynamic configuration.
GStreamer does use ffmpeg, but it just wraps the core encoder/decoder/filter code and discards the streams/graph/pipe part of ffmpeg.
Comment by Sesse__ 4 days ago
FFmpeg doesn't do “pipelines”. It's a library, not a framework.
Comment by mort96 3 days ago
Comment by mort96 3 days ago
"GStreamer" doesn't link against libav. The GStreamer plugin "gst-libav" links against libav. If you're not using the gst-libav, you're not using ffmpeg. I'd bet a relatively small amount of GStreamer use cases use gst-libav; I typically see people use e.g x264dec and x264enc (from the x264 plugin) to decode and encode media, or, for hardware encoding/decoding, the v4l2enc and v4l2dec elements (or elements from a SoC vendor's plug-in such as gst-rockchip). It also has its own non-libav elements to handle container formats, pixel format conversion, scaling, etc, which are more natural to use since they're part of the core gstreamer plug-in packages rather than gst-libav.
Comment by wmf 4 days ago
Comment by ranger_danger 4 days ago
Comment by cubefox 4 days ago
You would change your opinion quickly if your browser, apps and TV suddenly stopped supporting videos due to relying on FFmpeg.
Comment by defrost 4 days ago
It's okay for a sandbox to fall over due to bad inputs and poor memory security if it can just be restarted and move onto other streams.
Comment by ReactiveJelly 4 days ago
Thus:
1. Code which processes untrusted input
2. Code written in unsafe languages like C or C++
3. Code that runs without a sandbox
So ffmpeg should be sandboxed, same as the network code and GPU process are sandboxed.
Comment by defrost 4 days ago
Cheap arse low resource TVs should either include some form of sandboxing OR the entire device should be treated as a "can fall over" sandbox .. well isolated from any household LAN of consequence, etc.
It seems unlikely that BoxStore Brand Android TVs will be well designed with an eye to security so <shrug> they're an exercise for home net admin masochists and/or an opportunity to market sensible easy to use IoT age routers that come preconfigured to handle bad-device(s).
Comment by cubefox 4 days ago
Comment by mr_toad 3 days ago
Comment by bitwize 4 days ago
Comment by anonymousiam 4 days ago
Comment by erk__ 4 days ago
Comment by anon-3988 4 days ago
Comment by gerdesj 4 days ago
Yes, there are security issues but quite a few are not ffmpeg itself related - the input is pretty shabby or at least not exactly easy to deal with!
Obviously, they could do with some assistance and I'm sure you and I will both dive in with equal zeal.
Comment by RetroTechie 3 days ago
"Validate your inputs" is a common rule. Fuzzing is a thing. Both for good reasons (including security).
Comment by imjonse 4 days ago
They should prompt one of the more adventurous LLMs to find security bugs and with some luck it will deviate from the prompt and rewrite ffmpeg in Rust.
Comment by lionkor 4 days ago
Comment by guessmyname 4 days ago
A few months ago I started working on a system that finds critical security issues and opens PRs instead of just filing reports. The acceptance rate is sitting at roughly 94% so far. Most of the failures were due to project-specific kill switches or other internal mechanisms that weren’t documented, not because the vulnerability itself was misidentified.
Developers generally seem to prefer this approach. A bug report creates work. A good PR removes work. That sounds obvious, but a lot of security products still stop at the report and call it a day.
Comment by Rygian 4 days ago
Indeed: The industry optimizes for speed, time to market, and features, and applies the ostrich model to everything that doesn't bring short-time revenue (security considerations, accessibility, vendor lock-in, interoperability, …)
This has been going on for as long as the industry exists, and now we start to have the proper tools to assess the damage and understand the brittleness of it all.
Comment by rcbdev 4 days ago
Comment by tkocmathla 4 days ago
Comment by dgellow 3 days ago
Comment by rcbdev 4 days ago
Comment by nemothekid 4 days ago
Wow this is actually pretty serious - I'm even surprised its being published. There are several services where I can imagine this is exploitable today.
Comment by akerl_ 4 days ago
Comment by skupig 4 days ago
Comment by woodruffw 4 days ago
(There are a number of reasons for this, not least being that C makes it very easy to ship partially initialized memory over the wire.)
Comment by lostglass 4 days ago
Oh, and licensing. Licensing is the real killer. I could just write my own mp3 decoder easily (the format not the file type) but I'm not gonna risk my company getting sued into the ground by doing that.
Comment by throwaway2037 4 days ago
> I could just write my own mp3 decoder easily (the format not the file type) but I'm not gonna risk my company getting sued into the ground by doing that.
I am confused. > The MP3 format is now patent-free and requires no licensing fees to distribute or use. Fraunhofer IIS and Technicolor officially terminated their MP3 licensing programs, with all core patents having expired. Anyone can encode, decode, and distribute MP3 files or software without paying royalties.
Ref: https://forum.gamemaker.io/index.php?threads/do-you-still-ha...Ref: https://www.reddit.com/r/gamedev/comments/5stq8z/mp3_licensi...
Ref: https://www.audioblog.iis.fraunhofer.com/mp3-software-patent...
Comment by lostglass 3 days ago
Comment by throwaway2037 3 days ago
Comment by woodruffw 4 days ago
I agree about long periods of development and difficult standards, though.
Comment by hulitu 2 days ago
Don't tranform your ffmpeg instance into a web browser.
Comment by TiredOfLife 4 days ago
Comment by huflungdung 4 days ago
Comment by 0xbadcafebee 4 days ago
Comment by lschueller 4 days ago
Comment by da_chicken 4 days ago
Comment by journal 4 days ago
Comment by perlgeek 4 days ago
If a security bug is exploited in the wild, it's an n-day if it's been first exploited n days after the publication of the bug, and a zero-day if it's been exploited before or on the day of the publication.
When a bug is not yet exploited in the wild, it's just a discovery of a bug, not a zero-day.
Comment by dingaling 3 days ago
Originally a zero-day exploit was one that was found by crackers on the first day of release of a software product. Like finding a licence crack for a new Microsoft program on the day it went on sale.
There used to be fierce competition to find such an exploit within those 24 hours, and great kudos for those who did.
Nowadays a zero-day can apparently be found years after release, which makes no sense.
Comment by AlienRobot 3 days ago
Comment by da_chicken 3 days ago
> [Z]ero-day specifically compares when the white hats (vendors, system owners) and the black hats learn about the existence of a vulnerability. If white hats learn that a vulnerability exists by being subject to an in-the-wild black hat exploit of it, then it's a true zero-day.
And, again, you need to be aware that the vulnerability is the flaw or defect in the software or system (e.g., buffer overrun), and the exploit is the specific methodology that takes advantage of it (e.g., worm, malicious web request from a botnet, etc.).
Some people differentiate between a zero day vulnerability and a zero day exploit. I don't really find that is common anymore, and essentially everyone using it means zero-day exploit.
Comment by nerdsniper 4 days ago
Comment by da_chicken 4 days ago
I understand why it's poorly understood. It's a snappy term, and people assume it means "bad" and nothing else because that's all you can get from the context. However, since most people also don't know the difference between a vulnerability and an exploit, they won't understand the definition of a zero-day when they read it.
But I'm still going to complain if a security vulnerability research company is using the term incorrectly in their own press copy. It makes them look amateurish.
Comment by NooneAtAll3 4 days ago
is it the difference between a knife and a stab wound?
Comment by da_chicken 4 days ago
The vulnerability is the exposed weakness. Vulnerabilities get fixes, and they exist without anybody knowing about them. Vulnerabilities get CVEs assigned to them.
The exploit is the means of attack. It's the specific actions or calls that let you take advantage of a vulnerability. It could be a worm, or botnet scripts, or specifically crafted data[0]. A proof of concept is not an exploit itself, but it demonstrates that the vulnerability can be exploited.
An example of a vulnerability might be a gate where the gap between the door and the jam are too wide. The exploit is a coat hanger used to lift the inside latch from outside the gate. That results in unprivileged access.
And zero-day specifically compares when the white hats (vendors, system owners) and the black hats learn about the existence of a vulnerability. If white hats learn that a vulnerability exists by being subject to an in-the-wild black hat exploit of it, then it's a true zero-day.
Comment by jungfty 4 days ago
Comment by wavemode 4 days ago
Very serious, though in practice it doesn't sound like this bug achieves arbitrary RCE on its own (especially in the presence of ASLR). You would need there to be some writable and executable page of memory lying around.
Comment by skupig 4 days ago
Comment by jacobgold 4 days ago
But I can't think of a program more worthy of sandboxing when run with untrusted input than ffmpeg. It's a huge amount of C dealing with the most complicated video and audio codecs, which is notoriously impossible to get completely right.
But it's not actually that big of a problem. I run ffmpeg inside a VM or gVisor, and the end result is usually a video file that I'm perfectly willing to play in my browser, where it gets decoded in yet another sandbox because this shit is hard.
Comment by Terr_ 4 days ago
Secure sandboxing tends to mean opportunities to make unrestricted copies.
Comment by Gehinnn 4 days ago
Comment by thaumasiotes 4 days ago
Why would that be safe to assume? If that were a reasonable assumption, you could just as well assume that it's safe to run ffmpeg.
Comment by Denvercoder9 4 days ago
A manually run ffmpeg on the command line does nothing to restrict its privileges, and its security model has very little interest in doing so, while browsers very much have.
Comment by lostglass 4 days ago
And get hardware acceleration working...
Comment by ttoinou 4 days ago
Comment by kjs3 4 days ago
It's 'safe to assume' it's not. It's emphatically not safe to assume any mitigation is perfect.
Comment by preg_match 3 days ago
Comment by cyberax 4 days ago
Comment by techscruggs 3 days ago
It's hard not to see them as bottom feeders of the software industry and I wish we would starting treating them like pariah. Submit the PR or STFU.
Comment by br0ceph 3 days ago
Comment by ttoinou 4 days ago
If the attackers of ffmpeg need to be using such those authors’ services to find RCE in popular tools to attack, what the ffmpeg team needs to defeat attackers is to reduce efficiency of such tools depthfirst
Comment by Davidzheng 4 days ago
Comment by codedokode 3 days ago
Again there is another vulnerability caused by unchecked addition, and still modern languages like Rust or Go do not raise exception on overflow, and modern CPU architectures like RISC-V provide no overflow traps. And older languages like C or C++ do not have overflow checks also.
Ridiculous. It is obvious that humans cannot be trusted with writing correct arithmetics code.
Comment by heinrich5991 3 days ago
Rust's default integer overflow in release mode is defined as well, it'll just wrap around. This makes it less likely to result in a vulnerability (unless you start writing unsafe Rust).
Comment by pornel 3 days ago
Comment by uecker 3 days ago
IMHO better C tooling would be a far better investment than rewriting in Rust.
Comment by pornel 2 days ago
Note that C's tools for this like Valgrind, instrumented allocators and LLVM sanitizers work with Rust too. Investment in catching these in C generally helps unsafe Rust too, but Rust also has Miri, and a much better baseline for static analysis.
Comment by zahlman 3 days ago
Comment by steveklabnik 3 days ago
Comment by AlienRobot 3 days ago
Rust doesn't raise overflow by default. But you can just 123.checked_add(321). Now your code is unreadable, but it's overflow safe.
Honestly, based on the way I write code I'd rather something like an end of line comment. Like:
var x = y + z; # wrapped
Because I'm very unlikely to mix wrapped/checked/clamped arithmetic in a single line. It can't be a compiler state like doing(wrapped) { x + y } because in Zig every line must be "compilable" by itself, without requiring context from other parts of the code. Function names are too verbose. Casting is too verbose. Having a statement-level modifier would be a good compromise.Comment by codedokode 3 days ago
Comment by zahlman 3 days ago
Comment by snvzz 3 days ago
Irrelevant. Simplicity here is better than complexity.
Unless you're handwriting assembly, this is a compiler's job.
Comment by fizzynut 4 days ago
LLM constantly confidently giving me this same sounding script with a "the root cause" and how it "is simple" while being completely incorrect.
Comment by lostglass 4 days ago
In and of itself there's not a massive issue from what I can see, they're entry vectors that can lead to worse situations.
That's not to say they're not serious but if a Russian hacking group is using one of them it's in conjunction with other exploits or security flaws. Which is common in practice when it comes to decoding.
Comment by josephg 4 days ago
Comment by bayouborne 4 days ago
Comment by jeffbee 4 days ago
Comment by omoikane 4 days ago
Comment by appleappleapple 4 days ago
Comment by bethekidyouwant 4 days ago
Comment by fpoling 4 days ago
Another option is WASM or WASM-style sandboxes if using another process is undesirable.
Comment by johnnythunder 4 days ago
Comment by ttoinou 4 days ago
But are the compiler+OS that runs the ffmpeg executable really a sandbox ?
Comment by fpoling 4 days ago
The most secure way presently is to use qubes-os that allows to use a very hardened VM to run individual applications.
Comment by loeg 4 days ago
Comment by deafpolygon 4 days ago
Comment by kodt 4 days ago
Comment by tom_ 4 days ago
What about "ls"?
Comment by throwaway2037 3 days ago
Comment by hanzewei_asa 4 days ago
Comment by aaron695 4 days ago
Comment by jungfty 4 days ago