<UncleSniper>
There seems to be talk about FileUtils::rm_r being insecure, but nobody seems to actually be willing to describe the underlying issue. Why would that be insecure? -- Oooor, as a hook to short-circuit the question, could the problem be that people implemented 'rm -rH' (and yes, I know that's not a thing -- my point exactly, really) instead of 'rm
<constxd>
i just went down a rabbit hole trying to find the answer
<constxd>
the cve database linked to by the ruby docs gives a very vague description of the vulnerability followed by a dozen links all of which are broken or provide no additional details
<UncleSniper>
constxd: IKR?
<constxd>
some form of TOCTTOU vulnerability involving setuid binaries and/or symlinks
<mange>
I don't know what you mean by -rH, so it's hard for me to know how to answer. I think it's essentially a TOCTOU issue, where a symlink is changed between the stat call and the recursive walking of its contents.
<constxd>
i guess he means like -H from ls(1) where it follows symlinks
<UncleSniper>
mange: -H usually means --follow-symlinks -- but the very reason rm(1) doesn't provide that is because it can only cause tears.
<UncleSniper>
Welp, looking at the source, it kind of seems like the so-called problem only manifests if (by which I mean "when") people inevitably don't get how st_mode/umask(2) works. Not sure what that has to do with symlinks. If you move your files into a directory being deleted, that's on you.
<constxd>
i also have no idea what this means or how it resolves the vulnerability: If the target path points to a directory, this method uses methods File#chown and File#chmod in removing directories.
<UncleSniper>
That said, if Ruby's unlink actually does unlink(2) (which the docs claim it does), it shouldn't be an issue.
<UncleSniper>
constxd: Exactly. So the problem is that people follow symlinks when deleting -- which is basically the equivalent of firing a shotgun into the "ends" of 12 pipes without knowing where the pipes lead.
<constxd>
even then i'm curious how this can be used by another untrusted user to somehow create setuid binaries with the user id of the user calling FileUtils#rm_r
<constxd>
lmfao
<constxd>
the docs for remove_entry_secure read: Avoids a local vulnerability that can exist in certain circumstances;
<ruby[bot]>
UncleSniper: we in #ruby do not like pastebin.com, it loads slowly for most, has ads which are distracting and has terrible formatting. Please use https://gist.github.com or https://dpaste.org/
<UncleSniper>
constxd: Yes, that's what I'm saying. Nobody seems to know what the problem is really about. And then there's the guy that posts some C code (very little of which makes sense) and afterwards just says: "And now you are root". What?
<UncleSniper>
Somehow people seem to connect unlink(2)/rmdir(2) to creating setuid-root binaries (presumably of arbitrary contents). This all seems very weird to me.
<UncleSniper>
Going by the source, FileUtils::rm_r (with 'secure: nil') simply seems to do what one would expect 'rm -r' to do. No clue what issue people have with that. Yes, some joker could move their own files there, but that's like pointing a gun at your own head and then complaining about dying.
eddof13 has quit [Quit: eddof13]
UncleSniper has quit [Ping timeout: 256 seconds]
UncleSniper52 has joined #ruby
<UncleSniper52>
Woof, my 10-year-old RAM modules crashed my machine again. I should probably take that 4000 Euro rig I ordered out of the package one of these days...
<constxd>
idk i give up trying to figure out how the vulnerability works
Tempesta has joined #ruby
Tempesta has quit [Changing host]
Tempesta has joined #ruby
Tempesta has quit [Excess Flood]
<constxd>
but what is the point of this if statement
<mange>
Looking at how the walk in Entry_#postorder_traverse works, it uses lstat to check if the thing is a directory, then enumerates the children and walks, right? If the directory was replaced with a symlink between the #directory? call and the #entries call then Ruby would walk the symlink target, instead of removing the symlink itself.
Tempesta has joined #ruby
Tempesta has quit [Changing host]
Tempesta has joined #ruby
Tempesta has quit [Excess Flood]
<constxd>
if symlink? remove_file elsif directory? remove_dir else remove_file
Tempesta has joined #ruby
Tempesta has quit [Changing host]
Tempesta has joined #ruby
Tempesta has quit [Excess Flood]
Tempesta has joined #ruby
Tempesta has quit [Changing host]
Tempesta has joined #ruby
Tempesta has quit [Excess Flood]
<UncleSniper52>
constxd: Exactly. My takeaway from that commit is "use lstat(2) instead of stat(2)". Well, doy. The 'if' construct you posted make is very clear: Someone forgot that symlinks are a thing when implementing this.
<UncleSniper52>
The part where chmod is used is the part (hence the two CVEs) where someone might move stuff around. Sure, you could. But again, that really only means that you'll get your stuff deleted.
<mange>
You can symlink anywhere, though? Not just to your own stuff?
<UncleSniper52>
mange: Depends on the permissions. That's why I said, people don't seem to get how umask works.
<UncleSniper52>
I guess you could make root delete stuff on your behalf, but really, that not even remotely (in fact, it's the opposite) the same as getting root to exec(2) your code.
<UncleSniper52>
mange: I actually worked for a company once, in which people generally set their umask 0777. OK, you can do that. Why, though?
<mange>
I haven't seen the claim of getting root to exec your code, but getting root to delete things on your behalf is still a vulnerability. Also, the comment in ruby/lib/fileutils.rb does say the vulnerability requires "a world writable descendant directory", so bad permissions are definitely a compounding factor.
<mange>
Sorry, not compounding, necessary.
mattf has quit [Ping timeout: 260 seconds]
mattf has joined #ruby
<UncleSniper52>
mange: OK, but really, if a file('s name) getting deleted is an issue, you have much bigger problems. We're talking people having . in their $PATH here (and yes, that same company had that as a default, as in /etc/profile).
<UncleSniper52>
(Unless, of course, you have world-writable directories in your $PATH)
<mange>
I'm not really sure of the point you're making. Are you saying "it's not a big deal for rm_r to have a TOCTOU vulnerability because you should only delete things in directories you know other users can't write to"?
<UncleSniper52>
I'm saying that if this is an issue, it's not Ruby's issue but your file permissions' issue.
<UncleSniper52>
Again, if other people can write to the dir you're deleting, that's kinda seems like jumping on front of a bullet. If the system is set up correctly, root unwittingly deleting a file can only reduce capabilities, never raise them.
<mange>
Sure, but DOS is also a type of attack. Removing /etc/sudoers would be pretty annoying, and could deny admins access to things.
<mange>
Even beyond the root case, if I have a shared directory with another local user, I wouldn't expect running rm_r to delete everything in $HOME.
<mange>
s/running rm_r/running rm_r in the shared directory/
<UncleSniper52>
Moving /etc/sudoers to a directory being removed would require write permission to /etc, which non-root wouldn't have, right?
<UncleSniper52>
And again, root/the_other_user moving their stuff into to path of your 'rm -rf' bullet is on them, not you
<mange>
The TOCTOU attack means you can symlink to it, so if you can trick a root process into deleting a directory you can write to you can also trick it into deleting /etc/sudoers (or all of /etc).
Rounin has quit [Ping timeout: 244 seconds]
<UncleSniper52>
mange: ...if and only of your deletion follows symlinks, though, yeah? Which is exactly my point that it shouldn't. Which is exactly why rm(1) doesn't -- by which I mean can't.
<mange>
The trick is that you replace a directory with a symlink between the Ruby process running Entry_#directory? and Entry_#entries.
<UncleSniper52>
Ah. See, now it makes more sense.
<mange>
That's the vulnerability. At the Time Of Check it's a directory, but then at Time Of Use it's a symlink.
<UncleSniper52>
Still it requires people to be able to write to your stuff. If they can, that's on you.
<mange>
So then we come back to me wondering if your point is "it's not a big deal for rm_r to have a TOCTOU vulnerability because you should only delete things in directories you know other users can't write to". :)
<mange>
I think it's still something that should be fixed, even if it can be mitigated in other ways. The equivalent Perl CVE got a 2.6 LOW rating, so it's not *that* serious, but it's still not ideal.
<UncleSniper52>
How about we phrase it the other way around? "If you have to assume people can write to the dirs affected, consider that you might be deleting any and all thing you have to power to delete"?
<mange>
I think that sounds unreasonable, and that Ruby should not have that behaviour.
<UncleSniper52>
Well, chmodding random dirs doesn't seem like a good solution, either.
<mange>
If it helps, they're only directories you're about to delete.
<UncleSniper52>
I guess the one true fix is for the filesystem driver API to provide the 'rm -r' function straight up
<UncleSniper52>
mange: Are they?
<mange>
I guess not, if it fails on one of the children.
<mange>
I haven't looked at what happens in that case.
<UncleSniper52>
raise ArgumentError, "parent directory is world writable,..."
<UncleSniper52>
The problem is ancestors being world-writable, not descendants
Rounin has joined #ruby
Rounin has joined #ruby
<UncleSniper52>
Makes sense, since then someone could simply replace the whole dir you want to delete
<UncleSniper52>
...bla, bla, induction, bla
<UncleSniper52>
So like I said, unless the kernel makes this happen, there's no good solution
<mange>
Mmm. That's a bit of a sad conclusion. :(
<UncleSniper52>
Yeah, messing about with the ancestors -- until your process[ group] gets kill(2)ed, then then what?
<UncleSniper52>
The old story -- just because you say 'ensure' doesn't ensure anything
konsolebox has joined #ruby
<constxd>
am i missing something or even with that fix the same vulnerability still exists
<UncleSniper52>
That's what I'm saying: If the kernel doesn't do it, you probably can't
<UncleSniper52>
That's pretty much why O_EXCL was invented
<UncleSniper52>
and futex(2)
<constxd>
i think u gotta like open(O_NOFOLLOW), fstat(),and if it's a directory you fchdir() or something
<constxd>
or use the *at() variants of syscalls somehow
<UncleSniper52>
yes, *at would probably fix it -- then even if someone swaps out the name you opened, it won't affect you
<constxd>
anything that involves consecutive syscalls using the same file path is probably going to be susceptible to some kind TOCTTOU thing
<UncleSniper52>
Yeah, so script languages probably have a problem right there
<UncleSniper52>
unless they actually support the whole "once you open it" thing
<constxd>
but really you shouldn't have to do such careful analysis just to delete a folder lol, it's like you said the OS should expose a simple API for this common operation
<UncleSniper52>
right?
<UncleSniper52>
the issue is, even readdir(3) is not readdir(2)
<UncleSniper52>
If you want to do it right, you basically have to resort to assembler
<UncleSniper52>
cf. futex
<constxd>
need some kind of writev-like thing but for filesystem operations and the whole vector of operations is carried out atomically
<UncleSniper52>
I guess
<UncleSniper52>
but it's 3:43 over here and I have to get up at 7:40 for work. After that, my brother expects me to show up for the whole "Hallowe'en" thing, and it's like a 2 hour drive. So yeah... *mic drop*
brokkoli_origin has quit [Remote host closed the connection]
UncleSniper52 has left #ruby [#ruby]
brokkoli_origin has joined #ruby
brokkoli_origin has quit [Ping timeout: 252 seconds]
hightower2 has quit [Remote host closed the connection]
hightower2 has joined #ruby
Linux_Kerio has quit [Ping timeout: 252 seconds]
ua__ has joined #ruby
ua_ has quit [Read error: Connection reset by peer]
Sampersand has quit [Quit: Client closed]
ua__ has quit [Read error: Connection reset by peer]
hwpplayer1 has quit [Quit: I'll be back later]
eddof13 has quit [Quit: eddof13]
Linux_Kerio has joined #ruby
ua_ has joined #ruby
eddof13 has joined #ruby
eddof13 has quit [Quit: eddof13]
TomyLobo has joined #ruby
Vonter has quit [Ping timeout: 260 seconds]
<rhrf>
Hello, I am working on setting up Rails for a book I am reading. I run "rails _7.0.4_ new demo" and it starts creating the project. When I see the output at the end it still says using Rails 7.0.8. I do rails about and it still says 7.0.8. I cat the gem file and it says to use gem 7.0.4, but I am not sure why it is not doing that from the start. I am trying to figure out why and how to correct it. Any
<rhrf>
assistance would be appreciated.
cappy has quit [Quit: Leaving]
user71 has quit [Quit: Leaving]
<constxd>
rhrf: bro i think u are supposed to use bin/rails
<constxd>
creating a new rails app doesn't change your rails version globally
<constxd>
u prob have 7.0.4 at demo/bin/rails
<rhrf>
I tried bin/rails while in the directory and it says the samething.
<rhrf>
Hmm, unless my path is pointing to a different bin.
<constxd>
nah if the command contains any slashes then PATH is irrelevant
<rhrf>
Could it be because I am using asdf?
<constxd>
idk i've never used ruby or rails
<constxd>
what if u `bundle install --gemfile`
<constxd>
seems like `rails` automagically checks if u are in a rails project dir and if so it defers to bin/rails or something
<constxd>
classic rails Convention Over Configuration magic
<rhrf>
No change with with bundle install.
<constxd>
that seems odd
<constxd>
i guess `which -a rails` lists some kind of asdf shim?
<rhrf>
Yes
ruby[bot] has quit [Remote host closed the connection]